@automattic/jetpack-ai-client 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.7] - 2023-09-11
9
+ ### Added
10
+ - AI Client: add and expose reset() from useAiSuggestions() hook [#32886]
11
+ - AI Client: introduce audio duration display component [#32825]
12
+
8
13
  ## [0.1.6] - 2023-09-04
9
14
  ### Added
10
15
  - AI Client: add play and pause icons [#32788]
@@ -110,6 +115,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
110
115
  - Updated package dependencies. [#31659]
111
116
  - Updated package dependencies. [#31785]
112
117
 
118
+ [0.1.7]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.6...v0.1.7
113
119
  [0.1.6]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.5...v0.1.6
114
120
  [0.1.5]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.4...v0.1.5
115
121
  [0.1.4]: https://github.com/Automattic/jetpack-ai-client/compare/v0.1.3...v0.1.4
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "@automattic/jetpack-ai-client",
4
- "version": "0.1.6",
4
+ "version": "0.1.7",
5
5
  "description": "A JS client for consuming Jetpack AI services",
6
6
  "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/ai-client/#readme",
7
7
  "bugs": {
@@ -32,7 +32,7 @@
32
32
  ".": "./index.ts"
33
33
  },
34
34
  "dependencies": {
35
- "@automattic/jetpack-base-styles": "^0.6.8",
35
+ "@automattic/jetpack-base-styles": "^0.6.9",
36
36
  "@automattic/jetpack-connection": "^0.29.9",
37
37
  "@automattic/jetpack-shared-extension-utils": "^0.11.4",
38
38
  "@microsoft/fetch-event-source": "2.0.1",
@@ -0,0 +1,37 @@
1
+ /*
2
+ * External dependencies
3
+ */
4
+ import { useState, useEffect } from '@wordpress/element';
5
+ /*
6
+ * Internal dependencies
7
+ */
8
+ import { formatTime, getDuration } from './lib/media';
9
+ /*
10
+ * Types
11
+ */
12
+ import type React from 'react';
13
+
14
+ type AudioDurationDisplayProps = {
15
+ url: string;
16
+ };
17
+
18
+ /**
19
+ * AudioDurationDisplay component.
20
+ *
21
+ * @param {AudioDurationDisplayProps} props - Component props.
22
+ * @returns {React.ReactElement} Rendered component.
23
+ */
24
+ export default function AudioDurationDisplay( {
25
+ url,
26
+ }: AudioDurationDisplayProps ): React.ReactElement {
27
+ const [ duration, setDuration ] = useState( 0 );
28
+ useEffect( () => {
29
+ if ( ! url ) {
30
+ return;
31
+ }
32
+
33
+ getDuration( url ).then( setDuration );
34
+ }, [ url ] );
35
+
36
+ return <span>{ formatTime( duration, { addDecimalPart: false } ) }</span>;
37
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Function to get duration of audio file
3
+ *
4
+ * @param {string} url - The url of the audio file
5
+ * @returns {Promise<number>} The duration of the audio file
6
+ * @see https://stackoverflow.com/questions/21522036/html-audio-tag-duration-always-infinity
7
+ */
8
+ export function getDuration( url: string ): Promise< number > {
9
+ return new Promise( next => {
10
+ const tmpAudioInstance = new Audio( url );
11
+ tmpAudioInstance.addEventListener(
12
+ 'durationchange',
13
+ function () {
14
+ if ( this.duration === Infinity ) {
15
+ return;
16
+ }
17
+
18
+ const duration = this.duration;
19
+ tmpAudioInstance.remove(); // remove instance from memory
20
+ next( duration );
21
+ },
22
+ false
23
+ );
24
+
25
+ tmpAudioInstance.load();
26
+ tmpAudioInstance.currentTime = 24 * 60 * 60; // Fake big time
27
+ tmpAudioInstance.volume = 0;
28
+ tmpAudioInstance.play(); // This will call `durationchange` event
29
+ } );
30
+ }
31
+
32
+ type FormatTimeOptions = {
33
+ /**
34
+ * Whether to add the decimal part to the formatted time.
35
+ *
36
+ */
37
+ addDecimalPart?: boolean;
38
+ };
39
+
40
+ /**
41
+ * Formats the given time in milliseconds into a string with the format HH:MM:SS.DD,
42
+ * adding hours and minutes only when needed.
43
+ *
44
+ * @param {number} time - The time in seconds to format.
45
+ * @param {FormatTimeOptions} options - The arguments.
46
+ * @returns {string} The formatted time string.
47
+ * @example
48
+ * const formattedTime1 = formatTime( 1234567 ); // Returns "20:34.56"
49
+ * const formattedTime2 = formatTime( 45123 ); // Returns "45.12"
50
+ * const formattedTime3 = formatTime( 64, { addDecimalPart: false } ); // Returns "01:04"
51
+ */
52
+ export function formatTime(
53
+ time: number,
54
+ { addDecimalPart = true }: FormatTimeOptions = {}
55
+ ): string {
56
+ time = time * 1000;
57
+ const hours = Math.floor( time / 3600000 );
58
+ const minutes = Math.floor( time / 60000 ) % 60;
59
+ const seconds = Math.floor( time / 1000 ) % 60;
60
+ const deciseconds = Math.floor( time / 10 ) % 100;
61
+
62
+ const parts = [
63
+ hours > 0 ? hours.toString().padStart( 2, '0' ) + ':' : '',
64
+ hours > 0 || minutes > 0 ? minutes.toString().padStart( 2, '0' ) + ':' : '',
65
+ seconds.toString().padStart( 2, '0' ),
66
+ ];
67
+
68
+ if ( addDecimalPart ) {
69
+ parts.push( '.' + deciseconds.toString().padStart( 2, '0' ) );
70
+ }
71
+
72
+ return parts.join( '' );
73
+ }
@@ -1,2 +1,3 @@
1
1
  export { default as AIControl } from './ai-control';
2
2
  export { default as AiStatusIndicator } from './ai-status-indicator';
3
+ export { default as AudioDurationDisplay } from './audio-duration-display';
@@ -36,6 +36,7 @@ An object with the following properties:
36
36
  - `requestingState: RequestingStateProp`: The state of the request.
37
37
  - `eventSource: SuggestionsEventSource | undefined`: The event source of the request.
38
38
  - `request: ( prompt: Array< PromptItemProps >, options: AskQuestionOptionsArgProps ) => Promise< void >`: The request handler.
39
+ - `reset`: `() => void`: Reset the request state.
39
40
 
40
41
  ### `PromptItemProps`
41
42
 
@@ -98,6 +98,11 @@ type useAiSuggestionsProps = {
98
98
  */
99
99
  request: ( prompt: PromptProp, options?: AskQuestionOptionsArgProps ) => Promise< void >;
100
100
 
101
+ /*
102
+ * Reset the request state.
103
+ */
104
+ reset: () => void;
105
+
101
106
  /*
102
107
  * The handler to stop a suggestion.
103
108
  */
@@ -294,6 +299,17 @@ export default function useAiSuggestions( {
294
299
  ]
295
300
  );
296
301
 
302
+ /**
303
+ * Reset the request state.
304
+ *
305
+ * @returns {void}
306
+ */
307
+ const reset = useCallback( () => {
308
+ setRequestingState( 'init' );
309
+ setSuggestion( '' );
310
+ setError( undefined );
311
+ }, [] );
312
+
297
313
  /**
298
314
  * Stop suggestion handler.
299
315
  *
@@ -358,9 +374,10 @@ export default function useAiSuggestions( {
358
374
  error,
359
375
  requestingState,
360
376
 
361
- // Request/stop handlers
377
+ // Requests handlers
362
378
  request,
363
379
  stopSuggestion,
380
+ reset,
364
381
 
365
382
  // SuggestionsEventSource
366
383
  eventSource: eventSourceRef.current,
@@ -10,11 +10,13 @@ Based on [MediaRecorder](https://developer.mozilla.org/en-US/docs/Web/API/MediaR
10
10
 
11
11
  The hook returns an object with the following properties and methods:
12
12
 
13
- - `start: ( timeslice ) => void`: Start the media recording
14
- - `pause: () => void`: Pause the current media recording
15
- - `resume: () => void`: Resume a paused recording
16
- - `stop: () => void`: Stop the current recording
17
- - `state: 'inactive' | 'recording' | 'paused'`: Current recording state
13
+ - `state: 'inactive' | 'recording' | 'paused'` - Current recording state
14
+ - `blob`: `blob` - The recorded blob
15
+ - `url`: `string` - The recorded blob url
16
+ - `start: ( timeslice ) => void` - Start the media recording
17
+ - `pause: () => void` - Pause the current media recording
18
+ - `resume: () => void` - Resume a paused recording
19
+ - `stop: () => void` - Stop the current recording
18
20
 
19
21
  ## Example
20
22
 
@@ -22,6 +22,11 @@ type UseMediaRecordingReturn = {
22
22
  */
23
23
  blob: Blob | null;
24
24
 
25
+ /**
26
+ * The recorded blob url
27
+ */
28
+ url: string | null;
29
+
25
30
  /**
26
31
  * `start` recording handler
27
32
  */
@@ -195,6 +200,7 @@ export default function useMediaRecording( {
195
200
  return {
196
201
  state,
197
202
  blob,
203
+ url: blob ? URL.createObjectURL( blob ) : null,
198
204
 
199
205
  start,
200
206
  pause,