@exode-team/react-recorder 1.1.7 → 1.1.9

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.
Files changed (29) hide show
  1. package/dist/components/AudioRecorder.d.ts +1 -1
  2. package/dist/components/AudioRecorder.d.ts.map +1 -1
  3. package/dist/components/RecordingShell/RecordingShell.d.ts +1 -0
  4. package/dist/components/RecordingShell/RecordingShell.d.ts.map +1 -1
  5. package/dist/components/RecordingShell/RecordingShell.styled.d.ts +0 -2
  6. package/dist/components/RecordingShell/RecordingShell.styled.d.ts.map +1 -1
  7. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.d.ts +1 -0
  8. package/dist/components/VoiceMessagePlayer/VoiceMessagePlayer.d.ts.map +1 -1
  9. package/dist/components/WaveformBars/WaveformBars.d.ts +4 -0
  10. package/dist/components/WaveformBars/WaveformBars.d.ts.map +1 -1
  11. package/dist/components/WaveformBars/WaveformBars.styled.d.ts +3 -0
  12. package/dist/components/WaveformBars/WaveformBars.styled.d.ts.map +1 -0
  13. package/dist/hooks/useAudioRecorderHandlers.d.ts.map +1 -1
  14. package/dist/hooks/useMediaRecorder.d.ts.map +1 -1
  15. package/dist/hooks/useRecordingSender.d.ts +2 -1
  16. package/dist/hooks/useRecordingSender.d.ts.map +1 -1
  17. package/dist/hooks/useRecordingTiming.d.ts +2 -0
  18. package/dist/hooks/useRecordingTiming.d.ts.map +1 -1
  19. package/dist/hooks/useVoiceMessagePlayer.d.ts.map +1 -1
  20. package/dist/hooks/useVoiceRecorder.d.ts.map +1 -1
  21. package/dist/hooks/useVoiceRecorderInput.d.ts +1 -1
  22. package/dist/hooks/useVoiceRecorderInput.d.ts.map +1 -1
  23. package/dist/index.cjs +149 -54
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.js +149 -54
  26. package/dist/index.js.map +1 -1
  27. package/dist/types/recorder.d.ts +10 -2
  28. package/dist/types/recorder.d.ts.map +1 -1
  29. package/package.json +4 -4
@@ -12,7 +12,7 @@ interface Props extends Partial<RecorderOptionOverrides> {
12
12
  uploadId?: string;
13
13
  className?: string;
14
14
  onUploadError?: (error: unknown) => void;
15
- onUploaded?: (file: Partial<StorageFileEntity>) => void | Promise<void>;
15
+ onUploaded?: (file: Partial<StorageFileEntity>, duration: number) => void | Promise<void>;
16
16
  }
17
17
  declare const AudioRecorder: (props: Props) => any;
18
18
  export { AudioRecorder };
@@ -1 +1 @@
1
- {"version":3,"file":"AudioRecorder.d.ts","sourceRoot":"","sources":["../../src/components/AudioRecorder.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,OAAO,EAAiB,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAErF,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC/B,MAAM,UAAU,CAAC;AAGlB,KAAK,uBAAuB,GAAG,IAAI,CAC/B,uBAAuB,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,EACnD,UAAU,GAAG,YAAY,GAAG,UAAU,CACzC,CAAC;AAEF,UAAU,KAAM,SAAQ,OAAO,CAAC,uBAAuB,CAAC;IACpD,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,iBAAiB,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3E;AAGD,QAAA,MAAM,aAAa,GAAI,OAAO,KAAK,QAsHlC,CAAC;AAGF,OAAO,EAAE,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"AudioRecorder.d.ts","sourceRoot":"","sources":["../../src/components/AudioRecorder.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,OAAO,EAAiB,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAErF,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC/B,MAAM,UAAU,CAAC;AAGlB,KAAK,uBAAuB,GAAG,IAAI,CAC/B,uBAAuB,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,EACnD,UAAU,GAAG,YAAY,GAAG,UAAU,CACzC,CAAC;AAEF,UAAU,KAAM,SAAQ,OAAO,CAAC,uBAAuB,CAAC;IACpD,QAAQ,EAAE,cAAc,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACrD,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7F;AAGD,QAAA,MAAM,aAAa,GAAI,OAAO,KAAK,QAwHlC,CAAC;AAGF,OAAO,EAAE,aAAa,EAAE,CAAC"}
@@ -8,6 +8,7 @@ interface Props {
8
8
  isPaused: boolean;
9
9
  hasRecording: boolean;
10
10
  isPlaying: boolean;
11
+ lastDuration: number;
11
12
  recordingTime: number;
12
13
  playbackTime: number;
13
14
  playbackDuration: number;
@@ -1 +1 @@
1
- {"version":3,"file":"RecordingShell.d.ts","sourceRoot":"","sources":["../../../src/components/RecordingShell/RecordingShell.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AA2BH,UAAU,KAAK;IACX,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3C;AAGD,QAAA,MAAM,cAAc,GAAI,OAAO,KAAK,QA6HnC,CAAC;AAGF,OAAO,EAAE,cAAc,EAAE,CAAC"}
1
+ {"version":3,"file":"RecordingShell.d.ts","sourceRoot":"","sources":["../../../src/components/RecordingShell/RecordingShell.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AA2BH,UAAU,KAAK;IACX,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,uBAAuB,EAAE,MAAM,CAAC;IAChC,wBAAwB,EAAE,MAAM,CAAC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3C;AAGD,QAAA,MAAM,cAAc,GAAI,OAAO,KAAK,QA+JnC,CAAC;AAGF,OAAO,EAAE,cAAc,EAAE,CAAC"}
@@ -8,8 +8,6 @@ export declare const CancelButton: any;
8
8
  export declare const Track: any;
9
9
  export declare const CircleButton: any;
10
10
  export declare const Waveform: any;
11
- export declare const Bars: any;
12
- export declare const Bar: any;
13
11
  export declare const Timer: any;
14
12
  export declare const SendButton: any;
15
13
  export declare const SpinnerSlot: any;
@@ -1 +1 @@
1
- {"version":3,"file":"RecordingShell.styled.d.ts","sourceRoot":"","sources":["../../../src/components/RecordingShell/RecordingShell.styled.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,eAAO,MAAM,OAAO,KAOnB,CAAC;AAEF,eAAO,MAAM,YAAY,KAqBxB,CAAC;AAEF,eAAO,MAAM,KAAK,KAQjB,CAAC;AAEF,eAAO,MAAM,YAAY,KA2CxB,CAAC;AAEF,eAAO,MAAM,QAAQ,KAOpB,CAAC;AAEF,eAAO,MAAM,IAAI,KAMhB,CAAC;AAEF,eAAO,MAAM,GAAG,KAOf,CAAC;AAEF,eAAO,MAAM,KAAK,KAKjB,CAAC;AAEF,eAAO,MAAM,UAAU,KAyBtB,CAAC;AAEF,eAAO,MAAM,WAAW,KAMvB,CAAC;AAEF,eAAO,MAAM,OAAO,KAoBnB,CAAC"}
1
+ {"version":3,"file":"RecordingShell.styled.d.ts","sourceRoot":"","sources":["../../../src/components/RecordingShell/RecordingShell.styled.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,eAAO,MAAM,OAAO,KAOnB,CAAC;AAEF,eAAO,MAAM,YAAY,KAqBxB,CAAC;AAEF,eAAO,MAAM,KAAK,KAQjB,CAAC;AAEF,eAAO,MAAM,YAAY,KA2CxB,CAAC;AAEF,eAAO,MAAM,QAAQ,KAOpB,CAAC;AAEF,eAAO,MAAM,KAAK,KAKjB,CAAC;AAEF,eAAO,MAAM,UAAU,KAyBtB,CAAC;AAEF,eAAO,MAAM,WAAW,KAMvB,CAAC;AAEF,eAAO,MAAM,OAAO,KAoBnB,CAAC"}
@@ -6,6 +6,7 @@
6
6
  export interface VoiceMessagePlayerRef {
7
7
  play: () => void;
8
8
  prefetch: () => void;
9
+ seekToTime: (seconds: number) => void;
9
10
  }
10
11
  declare const VoiceMessagePlayer: any;
11
12
  export { VoiceMessagePlayer };
@@ -1 +1 @@
1
- {"version":3,"file":"VoiceMessagePlayer.d.ts","sourceRoot":"","sources":["../../../src/components/VoiceMessagePlayer/VoiceMessagePlayer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,MAAM,WAAW,qBAAqB;IAClC,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAeD,QAAA,MAAM,kBAAkB,KA8MtB,CAAC;AAGH,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"VoiceMessagePlayer.d.ts","sourceRoot":"","sources":["../../../src/components/VoiceMessagePlayer/VoiceMessagePlayer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,MAAM,WAAW,qBAAqB;IAClC,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAeD,QAAA,MAAM,kBAAkB,KAuNtB,CAAC;AAGH,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -8,11 +8,15 @@ interface Props {
8
8
  scale?: number;
9
9
  divider?: number;
10
10
  values: number[];
11
+ progress?: number;
11
12
  minHeight?: number;
12
13
  styleInterpolator?: (context: {
13
14
  normalized: number;
14
15
  height: number;
15
16
  index: number;
17
+ progress: number;
18
+ barProgress: number;
19
+ isPlayed: boolean;
16
20
  }) => React.CSSProperties;
17
21
  }
18
22
  declare const WaveformBars: (props: Props) => any;
@@ -1 +1 @@
1
- {"version":3,"file":"WaveformBars.d.ts","sourceRoot":"","sources":["../../../src/components/WaveformBars/WaveformBars.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,UAAU,KAAK;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC1B,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;KACjB,KAAK,KAAK,CAAC,aAAa,CAAC;CAC7B;AAGD,QAAA,MAAM,YAAY,GAAI,OAAO,KAAK,QA0BjC,CAAC;AAGF,OAAO,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"WaveformBars.d.ts","sourceRoot":"","sources":["../../../src/components/WaveformBars/WaveformBars.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,UAAU,KAAK;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC1B,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,OAAO,CAAC;KACrB,KAAK,KAAK,CAAC,aAAa,CAAC;CAC7B;AAGD,QAAA,MAAM,YAAY,GAAI,OAAO,KAAK,QAoCjC,CAAC;AAGF,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const Bars: any;
2
+ export declare const Bar: any;
3
+ //# sourceMappingURL=WaveformBars.styled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WaveformBars.styled.d.ts","sourceRoot":"","sources":["../../../src/components/WaveformBars/WaveformBars.styled.tsx"],"names":[],"mappings":"AAGA,eAAO,MAAM,IAAI,KAMhB,CAAC;AAEF,eAAO,MAAM,GAAG,KAOf,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAudioRecorderHandlers.d.ts","sourceRoot":"","sources":["../../src/hooks/useAudioRecorderHandlers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAG7E,UAAU,+BAA+B;IACrC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IAC9C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,EAAE,sBAAsB,CAAC;IACjC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C;AAGD,QAAA,MAAM,wBAAwB,GAAI,SAAS,+BAA+B;;;;;;CA2KzE,CAAC;AAGF,OAAO,EAAE,wBAAwB,EAAE,CAAC"}
1
+ {"version":3,"file":"useAudioRecorderHandlers.d.ts","sourceRoot":"","sources":["../../src/hooks/useAudioRecorderHandlers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAG7E,UAAU,+BAA+B;IACrC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;IAC9C,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,EAAE,sBAAsB,CAAC;IACjC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC5C;AAGD,QAAA,MAAM,wBAAwB,GAAI,SAAS,+BAA+B;;;;;;CAuKzE,CAAC;AAGF,OAAO,EAAE,wBAAwB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useMediaRecorder.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaRecorder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAGvD,UAAU,sBAAsB;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,QAAA,MAAM,gBAAgB,GAAI,WAAW,sBAAsB,KAAG,sBAkI7D,CAAC;AAGF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"useMediaRecorder.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaRecorder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAGvD,UAAU,sBAAsB;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,aAAa,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,QAAA,MAAM,gBAAgB,GAAI,WAAW,sBAAsB,KAAG,sBAsI7D,CAAC;AAGF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -9,8 +9,9 @@ interface UseRecordingSenderOptions<TFile> {
9
9
  mimeType: string;
10
10
  audioBlob: Blob | null;
11
11
  uploadId: string;
12
+ getDurationMs: () => number;
12
13
  uploader?: UploadStrategy<TFile>;
13
- onUploaded: (file: TFile) => void | Promise<void>;
14
+ onUploaded: (file: TFile, duration: number) => void | Promise<void>;
14
15
  deleteRecording: () => void;
15
16
  autoResetAfterUpload: boolean;
16
17
  onRecordingError?: (error: unknown) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useRecordingSender.d.ts","sourceRoot":"","sources":["../../src/hooks/useRecordingSender.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,UAAU,yBAAyB,CAAC,KAAK;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAGD,QAAA,MAAM,kBAAkB,GAAI,KAAK,GAAG,OAAO,EAAE,SAAS,yBAAyB,CAAC,KAAK,CAAC;;CA0DrF,CAAC;AAGF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"useRecordingSender.d.ts","sourceRoot":"","sources":["../../src/hooks/useRecordingSender.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,UAAU,yBAAyB,CAAC,KAAK;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;IACjC,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAC/C;AAGD,QAAA,MAAM,kBAAkB,GAAI,KAAK,GAAG,OAAO,EAAE,SAAS,yBAAyB,CAAC,KAAK,CAAC;;CA+DrF,CAAC;AAGF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -5,6 +5,8 @@
5
5
  */
6
6
  interface UseRecordingTimingResult {
7
7
  recordingTime: number;
8
+ lastDuration: number;
9
+ getElapsedMs: () => number;
8
10
  pauseSession: () => void;
9
11
  resetSession: () => void;
10
12
  resumeSession: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useRecordingTiming.d.ts","sourceRoot":"","sources":["../../src/hooks/useRecordingTiming.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,UAAU,wBAAwB;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,QAAA,MAAM,kBAAkB,QAAO,wBAgF9B,CAAC;AAGF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"useRecordingTiming.d.ts","sourceRoot":"","sources":["../../src/hooks/useRecordingTiming.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,UAAU,wBAAwB;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,IAAI,CAAC;CAC/B;AAED,QAAA,MAAM,kBAAkB,QAAO,wBAwG9B,CAAC;AAGF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useVoiceMessagePlayer.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoiceMessagePlayer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,4BAA4B,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAG1F,QAAA,MAAM,qBAAqB,GAAI,SAAS,4BAA4B,KAAG,2BA6JtE,CAAC;AAGF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"useVoiceMessagePlayer.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoiceMessagePlayer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,4BAA4B,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAG1F,QAAA,MAAM,qBAAqB,GAAI,SAAS,4BAA4B,KAAG,2BAqMtE,CAAC;AAGF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useVoiceRecorder.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoiceRecorder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAGhF,iBAAS,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,uBAAuB,CAAC,KAAK,CAAC,GAAG,sBAAsB,CAuMxG;AAGD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"useVoiceRecorder.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoiceRecorder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAGhF,iBAAS,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,KAAK,EAAE,uBAAuB,CAAC,KAAK,CAAC,GAAG,sBAAsB,CA2MxG;AAGD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
@@ -6,7 +6,7 @@
6
6
  import type { UploadStrategy } from '../types';
7
7
  interface UseVoiceRecorderInputOptions {
8
8
  uploader?: UploadStrategy<unknown>;
9
- onUploaded?: (file: unknown) => void | Promise<void>;
9
+ onUploaded?: (file: unknown, duration: number) => void | Promise<void>;
10
10
  onSend?: (file: File, duration: number) => void | Promise<void>;
11
11
  onError?: (error: unknown) => void;
12
12
  onCancel?: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useVoiceRecorderInput.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoiceRecorderInput.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,UAAU,4BAA4B;IAClC,QAAQ,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,QAAA,MAAM,qBAAqB,GAAI,SAAS,4BAA4B;;;;;;;;;;CA6MnE,CAAC;AAGF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
1
+ {"version":3,"file":"useVoiceRecorderInput.d.ts","sourceRoot":"","sources":["../../src/hooks/useVoiceRecorderInput.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG/C,UAAU,4BAA4B;IAClC,QAAQ,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,QAAA,MAAM,qBAAqB,GAAI,SAAS,4BAA4B;;;;;;;;;;CA6MnE,CAAC;AAGF,OAAO,EAAE,qBAAqB,EAAE,CAAC"}
package/dist/index.cjs CHANGED
@@ -276,6 +276,9 @@ const useMediaRecorder = (callbacks) => {
276
276
  if (!mediaRecorderRef.current || !isRecording) {
277
277
  return;
278
278
  }
279
+ if (mediaRecorderRef.current.state === 'paused') {
280
+ mediaRecorderRef.current.resume();
281
+ }
279
282
  mediaRecorderRef.current.stop();
280
283
  }, [isRecording]);
281
284
  const cancelRecording = React.useCallback(() => {
@@ -434,6 +437,7 @@ const useAudioPlayback = () => {
434
437
  * @author: exode <hello@exode.ru>
435
438
  */
436
439
  const useRecordingTiming = () => {
440
+ const [lastDuration, setLastDuration] = React.useState(0);
437
441
  const [recordingTime, setRecordingTime] = React.useState(0);
438
442
  const timerRef = React.useRef(null);
439
443
  const recordingStartRef = React.useRef(null);
@@ -461,6 +465,7 @@ const useRecordingTiming = () => {
461
465
  const startNewSession = React.useCallback(() => {
462
466
  recordingAccumulatedRef.current = 0;
463
467
  setRecordingTime(0);
468
+ setLastDuration(0);
464
469
  recordingStartRef.current = performance.now();
465
470
  startTicker();
466
471
  }, [startTicker]);
@@ -478,7 +483,21 @@ const useRecordingTiming = () => {
478
483
  setRecordingTime(roundTo(recordingAccumulatedRef.current));
479
484
  stopTicker();
480
485
  }, [stopTicker]);
486
+ const getElapsedMs = React.useCallback(() => {
487
+ const accumulated = recordingAccumulatedRef.current;
488
+ const elapsed = recordingStartRef.current !== null
489
+ ? (performance.now() - recordingStartRef.current) / 1000 + accumulated
490
+ : accumulated;
491
+ return Math.round(elapsed * 1000);
492
+ }, []);
481
493
  const resetSession = React.useCallback(() => {
494
+ const accumulated = recordingAccumulatedRef.current;
495
+ const elapsed = recordingStartRef.current !== null
496
+ ? (performance.now() - recordingStartRef.current) / 1000 + accumulated
497
+ : accumulated;
498
+ if (elapsed > 0) {
499
+ setLastDuration(roundTo(elapsed));
500
+ }
482
501
  stopTicker();
483
502
  recordingStartRef.current = null;
484
503
  recordingAccumulatedRef.current = 0;
@@ -490,6 +509,8 @@ const useRecordingTiming = () => {
490
509
  };
491
510
  }, [stopTicker]);
492
511
  return {
512
+ lastDuration,
513
+ getElapsedMs,
493
514
  pauseSession,
494
515
  resetSession,
495
516
  recordingTime,
@@ -591,7 +612,7 @@ const useRecorderVisualization = () => {
591
612
  * @author: exode <hello@exode.ru>
592
613
  */
593
614
  const useRecordingSender = (options) => {
594
- const { uploader, fileName, mimeType, uploadId, audioBlob, onUploaded, onRecordingError, deleteRecording, autoResetAfterUpload, } = options;
615
+ const { uploader, fileName, mimeType, uploadId, audioBlob, onUploaded, getDurationMs, onRecordingError, deleteRecording, autoResetAfterUpload, } = options;
595
616
  const sendRecording = React.useCallback(async () => {
596
617
  if (!audioBlob) {
597
618
  const error = new Error('Voice recording is not available');
@@ -604,11 +625,13 @@ const useRecordingSender = (options) => {
604
625
  throw error;
605
626
  }
606
627
  try {
628
+ const durationMs = getDurationMs();
629
+ const durationSec = durationMs / 1000;
607
630
  const file = new File([audioBlob], fileName, {
608
631
  type: audioBlob.type || mimeType,
609
632
  });
610
633
  const uploaded = await uploader(file, { uploadId });
611
- await onUploaded(uploaded);
634
+ await onUploaded(uploaded, durationSec);
612
635
  if (autoResetAfterUpload) {
613
636
  deleteRecording();
614
637
  }
@@ -624,8 +647,9 @@ const useRecordingSender = (options) => {
624
647
  audioBlob,
625
648
  mimeType,
626
649
  onUploaded,
627
- onRecordingError,
650
+ getDurationMs,
628
651
  deleteRecording,
652
+ onRecordingError,
629
653
  autoResetAfterUpload,
630
654
  ]);
631
655
  return { sendRecording };
@@ -641,7 +665,7 @@ function useVoiceRecorder(props) {
641
665
  const [audioBlob, setAudioBlob] = React.useState(null);
642
666
  const { audioURL, isPlaying, mountBlob, playbackTime, stopPlayback, resetPlayback, togglePlayback: _togglePlayback, playbackDuration, } = useAudioPlayback();
643
667
  const { audioLevels, previewLevels, resetAudioLevels, stopVisualization, resetPreviewLevels, resumeVisualization, cleanupVisualization, generatePreviewLevels, initializeVisualization, } = useRecorderVisualization();
644
- const { recordingTime, pauseSession, resetSession, resumeSession, startNewSession, } = useRecordingTiming();
668
+ const { lastDuration, getElapsedMs, recordingTime, pauseSession, resetSession, resumeSession, startNewSession, } = useRecordingTiming();
645
669
  const resetCurrentRecording = React.useCallback((opts) => {
646
670
  var _a;
647
671
  setAudioBlob(null);
@@ -669,10 +693,11 @@ function useVoiceRecorder(props) {
669
693
  mimeType,
670
694
  uploadId,
671
695
  uploader,
672
- autoResetAfterUpload,
673
696
  onUploaded,
674
- onRecordingError,
675
697
  deleteRecording,
698
+ onRecordingError,
699
+ autoResetAfterUpload,
700
+ getDurationMs: getElapsedMs,
676
701
  });
677
702
  const mountRecordingBlob = React.useCallback((blob) => {
678
703
  setAudioBlob(blob);
@@ -755,6 +780,7 @@ function useVoiceRecorder(props) {
755
780
  isPlaying,
756
781
  isRecording,
757
782
  audioLevels,
783
+ lastDuration,
758
784
  playbackTime,
759
785
  recordingTime,
760
786
  previewLevels,
@@ -1019,15 +1045,44 @@ const useVoiceMessagePlayer = (options) => {
1019
1045
  setIsPlaying(false);
1020
1046
  }
1021
1047
  }, [isLoading, stopThis, audioRef]);
1022
- const seekTo = React.useCallback((seekProgress) => {
1048
+ const seekTo = React.useCallback((seekPercent) => {
1023
1049
  const audio = audioRef.current;
1024
1050
  if (!audio || !isFinite(audio.duration)) {
1025
1051
  return;
1026
1052
  }
1027
- const time = seekProgress * audio.duration;
1053
+ const time = seekPercent * audio.duration;
1028
1054
  audio.currentTime = time;
1029
1055
  setCurrentTime(time);
1030
1056
  }, [audioRef]);
1057
+ /** Seek to a specific time in seconds and start playback */
1058
+ const seekToTime = React.useCallback((seconds) => {
1059
+ const audio = audioRef.current;
1060
+ if (audio && isFinite(audio.duration)) {
1061
+ audioPlaybackManager.stopOthers(stopThis);
1062
+ audio.currentTime = Math.max(0, Math.min(seconds, audio.duration));
1063
+ setCurrentTime(audio.currentTime);
1064
+ audio.play()
1065
+ .then(() => setIsPlaying(true))
1066
+ .catch(() => setIsPlaying(false));
1067
+ }
1068
+ else {
1069
+ void ensureAudioLoaded().then(() => {
1070
+ const loadedAudio = audioRef.current;
1071
+ if (loadedAudio) {
1072
+ audioPlaybackManager.stopOthers(stopThis);
1073
+ loadedAudio.currentTime = Math.max(0, Math.min(seconds, loadedAudio.duration || seconds));
1074
+ setCurrentTime(loadedAudio.currentTime);
1075
+ loadedAudio.play()
1076
+ .then(() => setIsPlaying(true))
1077
+ .catch(() => setIsPlaying(false));
1078
+ }
1079
+ });
1080
+ }
1081
+ }, [
1082
+ audioRef,
1083
+ stopThis,
1084
+ ensureAudioLoaded,
1085
+ ]);
1031
1086
  /** Sync external duration */
1032
1087
  React.useEffect(() => {
1033
1088
  if (externalDuration) {
@@ -1044,6 +1099,7 @@ const useVoiceMessagePlayer = (options) => {
1044
1099
  currentTime,
1045
1100
  waveformLevels,
1046
1101
  seekTo,
1102
+ seekToTime,
1047
1103
  prefetch,
1048
1104
  preload,
1049
1105
  formatTime,
@@ -1101,9 +1157,6 @@ const useAudioRecorderHandlers = (options) => {
1101
1157
  toggleRecordingPause,
1102
1158
  ]);
1103
1159
  const handleSend = React.useCallback(async () => {
1104
- if (isSending) {
1105
- return;
1106
- }
1107
1160
  shouldSendAfterStopRef.current = false;
1108
1161
  setIsSending(true);
1109
1162
  try {
@@ -1120,7 +1173,6 @@ const useAudioRecorderHandlers = (options) => {
1120
1173
  setIsSending(false);
1121
1174
  }
1122
1175
  }, [
1123
- isSending,
1124
1176
  stopPlayback,
1125
1177
  onUploadError,
1126
1178
  sendRecording,
@@ -1131,6 +1183,7 @@ const useAudioRecorderHandlers = (options) => {
1131
1183
  return;
1132
1184
  }
1133
1185
  if (isRecording) {
1186
+ setIsSending(true);
1134
1187
  shouldSendAfterStopRef.current = true;
1135
1188
  stopRecording();
1136
1189
  return;
@@ -1268,6 +1321,44 @@ const IdleControl = (props) => {
1268
1321
  return (jsxRuntime.jsxs(IdleWrapper, { children: [jsxRuntime.jsxs(IdleButton, { type: "button", onClick: onClick, "aria-label": startAriaLabel, children: [jsxRuntime.jsx(IdleButtonOutline, { "aria-hidden": true }), jsxRuntime.jsx(IdleButtonGlow, { "aria-hidden": true }), jsxRuntime.jsx(icons.Icon32Microphone, { height: 48, width: 48 })] }), jsxRuntime.jsx(IdleInstruction, { children: instruction })] }));
1269
1322
  };
1270
1323
 
1324
+ const Bars = styled.div `
1325
+ position: absolute;
1326
+ inset: 0;
1327
+ display: flex;
1328
+ align-items: flex-end;
1329
+ gap: 3px;
1330
+ `;
1331
+ const Bar = styled.div `
1332
+ flex: 1;
1333
+ min-width: 2px;
1334
+ border-radius: 9999px;
1335
+ background: var(--white, #ffffff);
1336
+ opacity: 0.6;
1337
+ transition: height 120ms ease, opacity 120ms ease;
1338
+ `;
1339
+
1340
+ const WaveformBars = (props) => {
1341
+ const { values, progress, styleInterpolator, scale = 40, minHeight = 6, divider = 100, } = props;
1342
+ return (jsxRuntime.jsx(Bars, { children: values.map((value, index) => {
1343
+ const normalized = clampValue(value);
1344
+ const height = Math.max(minHeight, (normalized / divider) * scale);
1345
+ const barProgress = values.length > 1 ? index / (values.length - 1) : 0;
1346
+ const isPlayed = (progress !== null && progress !== void 0 ? progress : 0) > 0 && barProgress <= (progress !== null && progress !== void 0 ? progress : 0);
1347
+ const interpolated = styleInterpolator === null || styleInterpolator === void 0 ? void 0 : styleInterpolator({
1348
+ normalized,
1349
+ height,
1350
+ index,
1351
+ progress: progress !== null && progress !== void 0 ? progress : 0,
1352
+ barProgress,
1353
+ isPlayed,
1354
+ });
1355
+ return (jsxRuntime.jsx(Bar, { style: {
1356
+ height: `${height}px`,
1357
+ ...interpolated,
1358
+ } }, index));
1359
+ }) }));
1360
+ };
1361
+
1271
1362
  /**
1272
1363
  * RecordingShell styles
1273
1364
  *
@@ -1362,21 +1453,6 @@ const Waveform = styled.div `
1362
1453
  align-items: center;
1363
1454
  overflow: hidden;
1364
1455
  `;
1365
- const Bars = styled.div `
1366
- position: absolute;
1367
- inset: 0;
1368
- display: flex;
1369
- align-items: flex-end;
1370
- gap: 3px;
1371
- `;
1372
- const Bar = styled.div `
1373
- flex: 1;
1374
- min-width: 2px;
1375
- border-radius: 9999px;
1376
- background: var(--white, #ffffff);
1377
- opacity: 0.6;
1378
- transition: height 120ms ease, opacity 120ms ease;
1379
- `;
1380
1456
  const Timer$1 = styled.span `
1381
1457
  text-align: right;
1382
1458
  font-size: 13px;
@@ -1438,31 +1514,33 @@ const Spinner = styled.span `
1438
1514
  }
1439
1515
  `;
1440
1516
 
1441
- const WaveformBars = (props) => {
1442
- const { values, styleInterpolator, scale = 40, minHeight = 6, divider = 100, } = props;
1443
- return (jsxRuntime.jsx(Bars, { children: values.map((value, index) => {
1444
- const normalized = clampValue(value);
1445
- const height = Math.max(minHeight, (normalized / divider) * scale);
1446
- const interpolated = styleInterpolator === null || styleInterpolator === void 0 ? void 0 : styleInterpolator({ normalized, height, index });
1447
- return (jsxRuntime.jsx(Bar, { style: {
1448
- height: `${height}px`,
1449
- ...interpolated,
1450
- } }, index));
1451
- }) }));
1452
- };
1453
-
1454
1517
  const RecordingShell = (props) => {
1455
1518
  const { isPaused, isPlaying, isSending, isRecording, audioLevels, hasRecording, playbackTime, previewLevels, recordingTime, stopAriaLabel, playAriaLabel, sendAriaLabel, startAriaLabel, cancelAriaLabel, playbackDuration, pausePlaybackAriaLabel, pauseRecordingAriaLabel, resumeRecordingAriaLabel, onSend, onCancel, formatTime, onRecordToggle, onTogglePlayback, } = props;
1519
+ const { lastDuration } = props;
1520
+ const isPreview = !isRecording || isPaused;
1456
1521
  const waveformValues = isRecording ? audioLevels : (hasRecording ? previewLevels : audioLevels);
1522
+ const effectiveDuration = recordingTime
1523
+ || playbackDuration
1524
+ || lastDuration
1525
+ || 1;
1526
+ const progress = (isPreview && isPlaying)
1527
+ ? Math.min(1, playbackTime / effectiveDuration)
1528
+ : 0;
1457
1529
  const displayTime = React.useMemo(() => {
1458
- if (isRecording) {
1530
+ if (isPlaying) {
1531
+ return formatTime(playbackTime);
1532
+ }
1533
+ if (isRecording && !isPaused) {
1534
+ return formatTime(recordingTime);
1535
+ }
1536
+ if (isRecording && isPaused) {
1459
1537
  return formatTime(recordingTime);
1460
1538
  }
1539
+ if (lastDuration > 0) {
1540
+ return formatTime(lastDuration);
1541
+ }
1461
1542
  if (hasRecording) {
1462
- if (isPlaying) {
1463
- return formatTime(playbackTime);
1464
- }
1465
- if (playbackDuration > 0) {
1543
+ if (playbackDuration > 0.5) {
1466
1544
  return formatTime(playbackDuration);
1467
1545
  }
1468
1546
  if (recordingTime > 0) {
@@ -1470,7 +1548,17 @@ const RecordingShell = (props) => {
1470
1548
  }
1471
1549
  }
1472
1550
  return formatTime(0);
1473
- }, [formatTime, hasRecording, isPlaying, playbackDuration, playbackTime, recordingTime]);
1551
+ }, [
1552
+ isPaused,
1553
+ isPlaying,
1554
+ formatTime,
1555
+ isRecording,
1556
+ lastDuration,
1557
+ hasRecording,
1558
+ playbackTime,
1559
+ recordingTime,
1560
+ playbackDuration,
1561
+ ]);
1474
1562
  const recordButtonDisabled = isSending;
1475
1563
  const recordButtonIcon = !isRecording || isPaused
1476
1564
  ? jsxRuntime.jsx(icons.Icon16Microphone, { width: 24, height: 24 })
@@ -1488,10 +1576,13 @@ const RecordingShell = (props) => {
1488
1576
  : jsxRuntime.jsx(icons.Icon16Play, { width: 24, height: 24 });
1489
1577
  const playbackAria = isPlaying ? pausePlaybackAriaLabel : playAriaLabel;
1490
1578
  const sendDisabled = isSending || (!hasRecording && !isRecording);
1491
- return (jsxRuntime.jsxs(Wrapper$1, { children: [jsxRuntime.jsx(CancelButton$1, { type: "button", onClick: onCancel, "aria-label": cancelAriaLabel, children: jsxRuntime.jsx(icons.Icon28DeleteOutlineAndroid, { fill: "var(--exode_red)", width: 24, height: 24, style: { minWidth: 24 } }) }), jsxRuntime.jsxs(Track, { children: [jsxRuntime.jsx(CircleButton, { type: "button", "$variant": recordButtonVariant, onClick: onRecordToggle, "aria-label": recordButtonAria, disabled: recordButtonDisabled, children: recordButtonIcon }), !canShowPlayback ? null : (jsxRuntime.jsx(CircleButton, { type: "button", "$variant": "play", onClick: onTogglePlayback, "aria-label": playbackAria, disabled: playbackDisabled, children: playbackIcon })), jsxRuntime.jsx(Waveform, { "$expanded": canShowPlayback, children: jsxRuntime.jsx(WaveformBars, { values: waveformValues, divider: 100, scale: 42, minHeight: 6, styleInterpolator: ({ normalized }) => ({
1579
+ return (jsxRuntime.jsxs(Wrapper$1, { children: [jsxRuntime.jsx(CancelButton$1, { type: "button", onClick: onCancel, "aria-label": cancelAriaLabel, children: jsxRuntime.jsx(icons.Icon28DeleteOutlineAndroid, { fill: "var(--exode_red)", width: 24, height: 24, style: { minWidth: 24 } }) }), jsxRuntime.jsxs(Track, { children: [jsxRuntime.jsx(CircleButton, { type: "button", "$variant": recordButtonVariant, onClick: onRecordToggle, "aria-label": recordButtonAria, disabled: recordButtonDisabled, children: recordButtonIcon }), !canShowPlayback ? null : (jsxRuntime.jsx(CircleButton, { type: "button", "$variant": "play", onClick: onTogglePlayback, "aria-label": playbackAria, disabled: playbackDisabled, children: playbackIcon })), jsxRuntime.jsx(Waveform, { "$expanded": canShowPlayback, children: jsxRuntime.jsx(WaveformBars, { scale: 42, minHeight: 6, divider: 100, progress: progress, values: waveformValues, styleInterpolator: ({ normalized, isPlayed, progress: p }) => ({
1492
1580
  height: `${12 + normalized / 2}px`,
1493
1581
  maxHeight: '34px',
1494
- opacity: 0.38 + Math.min(0.5, normalized / 120),
1582
+ opacity: p > 0
1583
+ ? (isPlayed ? 0.80 : 0.22)
1584
+ : 0.38 + Math.min(0.5, normalized / 120),
1585
+ transition: 'opacity 80ms ease',
1495
1586
  }) }) }), jsxRuntime.jsx(Timer$1, { children: displayTime })] }), jsxRuntime.jsx(SendButton$1, { type: "button", onClick: onSend, disabled: sendDisabled, "aria-label": sendAriaLabel, children: isSending ? (jsxRuntime.jsx(SpinnerSlot, { children: jsxRuntime.jsx(Spinner, {}) })) : (jsxRuntime.jsx(icons.Icon28SendOutline, { width: 24, height: 24 })) })] }));
1496
1587
  };
1497
1588
 
@@ -1574,9 +1665,9 @@ const AudioRecorder = (props) => {
1574
1665
  messages,
1575
1666
  onUploadError,
1576
1667
  });
1577
- const { isPaused, isPlaying, formatTime, audioLevels, isRecording, playbackTime, hasRecording, previewLevels, recordingTime, playbackDuration, } = recorder;
1578
- const showRecorderBar = isRecording || hasRecording;
1579
- return (jsxRuntime.jsx(Container, { className: className, children: showRecorderBar ? (jsxRuntime.jsx(RecordingShell, { isPaused: isPaused, isSending: isSending, isPlaying: isPlaying, formatTime: formatTime, onCancel: handleCancel, isRecording: isRecording, audioLevels: audioLevels, onSend: handleSendRequest, playbackTime: playbackTime, hasRecording: hasRecording, previewLevels: previewLevels, recordingTime: recordingTime, playbackDuration: playbackDuration, onRecordToggle: handleRecordToggle, sendAriaLabel: messages.buttons.send, onTogglePlayback: handlePlaybackToggle, cancelAriaLabel: messages.buttons.cancel, playAriaLabel: messages.aria.playRecording, stopAriaLabel: messages.aria.stopRecording, startAriaLabel: messages.aria.startRecording, pauseRecordingAriaLabel: messages.aria.pauseRecording, resumeRecordingAriaLabel: messages.aria.resumeRecording, pausePlaybackAriaLabel: messages.aria.pausePlayback })) : (jsxRuntime.jsx(IdleControl, { onClick: handleRecordToggle, startAriaLabel: messages.aria.startRecording, instruction: messages.instruction })) }));
1668
+ const { isPaused, isPlaying, formatTime, audioLevels, isRecording, lastDuration, playbackTime, hasRecording, previewLevels, recordingTime, playbackDuration, } = recorder;
1669
+ const showRecorderBar = isRecording || hasRecording || isSending;
1670
+ return (jsxRuntime.jsx(Container, { className: className, children: showRecorderBar ? (jsxRuntime.jsx(RecordingShell, { isPaused: isPaused, isSending: isSending, isPlaying: isPlaying, formatTime: formatTime, onCancel: handleCancel, isRecording: isRecording, audioLevels: audioLevels, onSend: handleSendRequest, playbackTime: playbackTime, lastDuration: lastDuration, hasRecording: hasRecording, previewLevels: previewLevels, recordingTime: recordingTime, playbackDuration: playbackDuration, onRecordToggle: handleRecordToggle, sendAriaLabel: messages.buttons.send, onTogglePlayback: handlePlaybackToggle, cancelAriaLabel: messages.buttons.cancel, playAriaLabel: messages.aria.playRecording, stopAriaLabel: messages.aria.stopRecording, startAriaLabel: messages.aria.startRecording, pauseRecordingAriaLabel: messages.aria.pauseRecording, resumeRecordingAriaLabel: messages.aria.resumeRecording, pausePlaybackAriaLabel: messages.aria.pausePlayback })) : (jsxRuntime.jsx(IdleControl, { onClick: handleRecordToggle, startAriaLabel: messages.aria.startRecording, instruction: messages.instruction })) }));
1580
1671
  };
1581
1672
 
1582
1673
  /**
@@ -2154,14 +2245,18 @@ const VoiceMessagePlayer = React.forwardRef((props, ref) => {
2154
2245
  VoicePlaybackChain.playNext(chainId);
2155
2246
  }
2156
2247
  }, [onEnded, chainId]);
2157
- const { play, progress, isLoaded, isPlaying, isLoading, currentTime, waveformLevels, seekTo, prefetch, preload, formatTime, togglePlayback, duration: resolvedDuration, } = useVoiceMessagePlayer({
2248
+ const { play, progress, isLoaded, isPlaying, isLoading, currentTime, waveformLevels, seekTo, seekToTime, prefetch, preload, formatTime, togglePlayback, duration: resolvedDuration, } = useVoiceMessagePlayer({
2158
2249
  src,
2159
2250
  duration,
2160
2251
  waveformData,
2161
2252
  preloadedBlob: props.preloadedBlob,
2162
2253
  onEnded: handleEnded,
2163
2254
  });
2164
- React.useImperativeHandle(ref, () => ({ play, prefetch }), [play, prefetch]);
2255
+ React.useImperativeHandle(ref, () => ({ play, prefetch, seekToTime }), [
2256
+ play,
2257
+ prefetch,
2258
+ seekToTime,
2259
+ ]);
2165
2260
  /** Auto-preload audio when player enters viewport */
2166
2261
  React.useEffect(() => {
2167
2262
  if (!autoPreload || isLoaded || isLoading) {
@@ -2215,7 +2310,7 @@ const VoiceMessagePlayer = React.forwardRef((props, ref) => {
2215
2310
  observer.disconnect();
2216
2311
  };
2217
2312
  }, []);
2218
- const displayTime = isPlaying
2313
+ const displayTime = (isPlaying || currentTime > 0)
2219
2314
  ? formatTime(currentTime)
2220
2315
  : formatTime(resolvedDuration);
2221
2316
  const handleTrackClick = React.useCallback((e) => {