@automattic/jetpack-ai-client 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/build/api-fetch/index.d.ts +7 -0
- package/build/api-fetch/index.js +6 -0
- package/build/ask-question/index.d.ts +2 -2
- package/build/ask-question/index.js +1 -1
- package/build/audio-transcription/index.d.ts +12 -0
- package/build/audio-transcription/index.js +51 -0
- package/build/components/ai-control/index.d.ts +7 -6
- package/build/components/ai-control/index.js +6 -6
- package/build/components/ai-control/message.d.ts +10 -0
- package/build/components/ai-control/message.js +15 -2
- package/build/components/ai-status-indicator/index.d.ts +1 -1
- package/build/components/audio-duration-display/index.d.ts +3 -2
- package/build/components/audio-duration-display/index.js +3 -14
- package/build/components/audio-duration-display/lib/media.d.ts +13 -14
- package/build/components/audio-duration-display/lib/media.js +7 -32
- package/build/components/index.d.ts +4 -4
- package/build/components/index.js +4 -4
- package/build/data-flow/context.d.ts +9 -6
- package/build/data-flow/context.js +1 -1
- package/build/data-flow/index.d.ts +3 -3
- package/build/data-flow/index.js +3 -3
- package/build/data-flow/use-ai-context.d.ts +3 -3
- package/build/data-flow/use-ai-context.js +3 -3
- package/build/data-flow/with-ai-assistant-data.js +2 -2
- package/build/hooks/use-ai-suggestions/index.d.ts +4 -4
- package/build/hooks/use-ai-suggestions/index.js +2 -2
- package/build/hooks/use-audio-transcription/index.d.ts +28 -0
- package/build/hooks/use-audio-transcription/index.js +56 -0
- package/build/hooks/use-media-recording/index.d.ts +34 -12
- package/build/hooks/use-media-recording/index.js +116 -41
- package/build/hooks/use-transcription-post-processing/index.d.ts +30 -0
- package/build/hooks/use-transcription-post-processing/index.js +84 -0
- package/build/icons/index.d.ts +7 -7
- package/build/icons/index.js +7 -7
- package/build/icons/mic.js +2 -2
- package/build/icons/player-pause.js +1 -1
- package/build/index.d.ts +12 -9
- package/build/index.js +12 -9
- package/build/jwt/index.js +4 -1
- package/build/suggestions-event-source/index.d.ts +1 -1
- package/build/suggestions-event-source/index.js +3 -3
- package/build/types.d.ts +9 -2
- package/build/types.js +4 -0
- package/package.json +5 -5
- package/src/api-fetch/index.ts +13 -0
- package/src/ask-question/index.ts +2 -2
- package/src/audio-transcription/index.ts +75 -0
- package/src/components/ai-control/index.tsx +12 -16
- package/src/components/ai-control/message.tsx +34 -2
- package/src/components/ai-control/style.scss +6 -0
- package/src/components/ai-status-indicator/index.tsx +1 -1
- package/src/components/audio-duration-display/index.tsx +6 -17
- package/src/components/audio-duration-display/lib/media.ts +17 -40
- package/src/components/index.ts +8 -4
- package/src/data-flow/context.tsx +7 -8
- package/src/data-flow/index.ts +3 -3
- package/src/data-flow/use-ai-context.ts +6 -6
- package/src/data-flow/with-ai-assistant-data.tsx +2 -2
- package/src/hooks/use-ai-suggestions/index.ts +6 -6
- package/src/hooks/use-audio-transcription/index.ts +95 -0
- package/src/hooks/use-media-recording/Readme.md +2 -2
- package/src/hooks/use-media-recording/index.ts +179 -59
- package/src/hooks/use-transcription-post-processing/index.ts +135 -0
- package/src/icons/index.ts +7 -7
- package/src/icons/mic.tsx +14 -16
- package/src/icons/player-pause.tsx +5 -5
- package/src/index.ts +12 -9
- package/src/jwt/index.ts +4 -1
- package/src/suggestions-event-source/index.ts +4 -4
- package/src/types.ts +29 -2
package/build/types.d.ts
CHANGED
|
@@ -13,13 +13,20 @@ export type PromptItemProps = {
|
|
|
13
13
|
};
|
|
14
14
|
export type PromptMessagesProp = Array<PromptItemProps>;
|
|
15
15
|
export type PromptProp = PromptMessagesProp | string;
|
|
16
|
-
export type { UseAiContextOptions } from './data-flow/use-ai-context';
|
|
17
|
-
export type { RequestingErrorProps } from './hooks/use-ai-suggestions';
|
|
16
|
+
export type { UseAiContextOptions } from './data-flow/use-ai-context.js';
|
|
17
|
+
export type { RequestingErrorProps } from './hooks/use-ai-suggestions/index.js';
|
|
18
|
+
export type { UseAudioTranscriptionProps, UseAudioTranscriptionReturn, } from './hooks/use-audio-transcription/index.js';
|
|
19
|
+
export type { UseTranscriptionPostProcessingProps, UseTranscriptionPostProcessingReturn, PostProcessingAction, } from './hooks/use-transcription-post-processing/index.js';
|
|
20
|
+
export { TRANSCRIPTION_POST_PROCESSING_ACTION_SIMPLE_DRAFT } from './hooks/use-transcription-post-processing/index.js';
|
|
18
21
|
export declare const REQUESTING_STATES: readonly ["init", "requesting", "suggesting", "done", "error"];
|
|
19
22
|
export type RequestingStateProp = (typeof REQUESTING_STATES)[number];
|
|
20
23
|
export declare const AI_MODEL_GPT_3_5_Turbo_16K: "gpt-3.5-turbo-16k";
|
|
21
24
|
export declare const AI_MODEL_GPT_4: "gpt-4";
|
|
22
25
|
export type AiModelTypeProp = typeof AI_MODEL_GPT_3_5_Turbo_16K | typeof AI_MODEL_GPT_4;
|
|
26
|
+
export type { RecordingState } from './hooks/use-media-recording/index.js';
|
|
27
|
+
export type CancelablePromise<T = void> = Promise<T> & {
|
|
28
|
+
canceled?: boolean;
|
|
29
|
+
};
|
|
23
30
|
interface JPConnectionInitialState {
|
|
24
31
|
apiNonce: string;
|
|
25
32
|
siteSuffix: string;
|
package/build/types.js
CHANGED
|
@@ -5,6 +5,10 @@ export const ERROR_CONTEXT_TOO_LARGE = 'error_context_too_large';
|
|
|
5
5
|
export const ERROR_NETWORK = 'error_network';
|
|
6
6
|
export const ERROR_UNCLEAR_PROMPT = 'error_unclear_prompt';
|
|
7
7
|
export const ERROR_RESPONSE = 'error_response';
|
|
8
|
+
/*
|
|
9
|
+
* Hook constants
|
|
10
|
+
*/
|
|
11
|
+
export { TRANSCRIPTION_POST_PROCESSING_ACTION_SIMPLE_DRAFT } from './hooks/use-transcription-post-processing/index.js';
|
|
8
12
|
/*
|
|
9
13
|
* Requests types
|
|
10
14
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "@automattic/jetpack-ai-client",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
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": {
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
},
|
|
23
23
|
"type": "module",
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@storybook/addon-actions": "7.6.
|
|
26
|
-
"@storybook/blocks": "7.6.
|
|
27
|
-
"@storybook/react": "7.6.
|
|
25
|
+
"@storybook/addon-actions": "7.6.17",
|
|
26
|
+
"@storybook/blocks": "7.6.17",
|
|
27
|
+
"@storybook/react": "7.6.17",
|
|
28
28
|
"jest": "^29.6.2",
|
|
29
29
|
"jest-environment-jsdom": "29.7.0",
|
|
30
30
|
"typescript": "5.0.4"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"types": "./build/index.d.ts",
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@automattic/jetpack-base-styles": "^0.6.17",
|
|
42
|
-
"@automattic/jetpack-connection": "^0.32.
|
|
42
|
+
"@automattic/jetpack-connection": "^0.32.3",
|
|
43
43
|
"@automattic/jetpack-shared-extension-utils": "^0.14.2",
|
|
44
44
|
"@microsoft/fetch-event-source": "2.0.1",
|
|
45
45
|
"@types/react": "18.2.33",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import apiFetchMod from '@wordpress/api-fetch';
|
|
5
|
+
|
|
6
|
+
// @wordpress/api-fetch (as of 6.47.0) declares itself in such a way that tsc and node see the function at apiFetchMod.default
|
|
7
|
+
// while some other environments (including code running inside WordPress itself) see it at apiFetch.
|
|
8
|
+
// See https://arethetypeswrong.github.io/?p=@wordpress/api-fetch@6.47.0
|
|
9
|
+
// This is a helper to simplify the usage of the api-fetch module on the ai-client package.
|
|
10
|
+
type ApiFetchType = typeof apiFetchMod.default;
|
|
11
|
+
const apiFetch: ApiFetchType = ( apiFetchMod.default ?? apiFetchMod ) as ApiFetchType;
|
|
12
|
+
|
|
13
|
+
export default apiFetch;
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import debugFactory from 'debug';
|
|
5
|
-
import SuggestionsEventSource from '../suggestions-event-source';
|
|
5
|
+
import SuggestionsEventSource from '../suggestions-event-source/index.js';
|
|
6
6
|
/*
|
|
7
7
|
* Types & constants
|
|
8
8
|
*/
|
|
9
|
-
import type { AiModelTypeProp, PromptProp } from '../types';
|
|
9
|
+
import type { AiModelTypeProp, PromptProp } from '../types.js';
|
|
10
10
|
|
|
11
11
|
export type AskQuestionOptionsArgProps = {
|
|
12
12
|
/*
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import debugFactory from 'debug';
|
|
5
|
+
/**
|
|
6
|
+
* Internal dependencies
|
|
7
|
+
*/
|
|
8
|
+
import apiFetch from '../api-fetch/index.js';
|
|
9
|
+
import requestJwt from '../jwt/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Types
|
|
12
|
+
*/
|
|
13
|
+
import { CancelablePromise } from '../types.js';
|
|
14
|
+
|
|
15
|
+
const debug = debugFactory( 'jetpack-ai-client:audio-transcription' );
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The response from the audio transcription service.
|
|
19
|
+
*/
|
|
20
|
+
type AudioTranscriptionResponse = {
|
|
21
|
+
/**
|
|
22
|
+
* The transcribed text.
|
|
23
|
+
*/
|
|
24
|
+
text: string;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A function that takes an audio blob and transcribes it.
|
|
29
|
+
*
|
|
30
|
+
* @param {Blob} audio - The audio to be transcribed, from a recording or from a file.
|
|
31
|
+
* @param {string} feature - The feature name that is calling the transcription.
|
|
32
|
+
* @returns {Promise<string>} - The promise of a string containing the transcribed audio.
|
|
33
|
+
*/
|
|
34
|
+
export default async function transcribeAudio(
|
|
35
|
+
audio: Blob,
|
|
36
|
+
feature?: string
|
|
37
|
+
// @ts-expect-error Promises are not cancelable by default
|
|
38
|
+
): CancelablePromise< string > {
|
|
39
|
+
debug( 'Transcribing audio: %o. Feature: %o', audio, feature );
|
|
40
|
+
|
|
41
|
+
// Get a token to use the transcription service
|
|
42
|
+
let token = '';
|
|
43
|
+
try {
|
|
44
|
+
token = ( await requestJwt() ).token;
|
|
45
|
+
} catch ( error ) {
|
|
46
|
+
debug( 'Error getting token: %o', error );
|
|
47
|
+
return Promise.reject( error );
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Build a FormData object to hold the audio file
|
|
51
|
+
const formData = new FormData();
|
|
52
|
+
formData.append( 'audio_file', audio );
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const headers = {
|
|
56
|
+
Authorization: `Bearer ${ token }`,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const response: AudioTranscriptionResponse = await apiFetch( {
|
|
60
|
+
url: `https://public-api.wordpress.com/wpcom/v2/jetpack-ai-transcription${
|
|
61
|
+
feature ? `?feature=${ feature }` : ''
|
|
62
|
+
}`,
|
|
63
|
+
method: 'POST',
|
|
64
|
+
body: formData,
|
|
65
|
+
headers,
|
|
66
|
+
} );
|
|
67
|
+
|
|
68
|
+
debug( 'Transcription response: %o', response );
|
|
69
|
+
|
|
70
|
+
return response.text;
|
|
71
|
+
} catch ( error ) {
|
|
72
|
+
debug( 'Transcription error response: %o', error );
|
|
73
|
+
return Promise.reject( error );
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -4,27 +4,23 @@
|
|
|
4
4
|
import { PlainText } from '@wordpress/block-editor';
|
|
5
5
|
import { Button, ButtonGroup } from '@wordpress/components';
|
|
6
6
|
import { useKeyboardShortcut } from '@wordpress/compose';
|
|
7
|
-
import {
|
|
8
|
-
forwardRef,
|
|
9
|
-
useImperativeHandle,
|
|
10
|
-
useRef,
|
|
11
|
-
useEffect,
|
|
12
|
-
useCallback,
|
|
13
|
-
} from '@wordpress/element';
|
|
7
|
+
import { useImperativeHandle, useRef, useEffect, useCallback } from '@wordpress/element';
|
|
14
8
|
import { __ } from '@wordpress/i18n';
|
|
15
9
|
import { Icon, closeSmall, check, arrowUp, trash, reusableBlock } from '@wordpress/icons';
|
|
16
10
|
import classNames from 'classnames';
|
|
11
|
+
import { forwardRef } from 'react';
|
|
17
12
|
import React from 'react';
|
|
18
13
|
/**
|
|
19
14
|
* Internal dependencies
|
|
20
15
|
*/
|
|
21
16
|
import './style.scss';
|
|
22
|
-
import AiStatusIndicator from '../ai-status-indicator';
|
|
23
|
-
import { GuidelineMessage } from './message';
|
|
17
|
+
import AiStatusIndicator from '../ai-status-indicator/index.js';
|
|
18
|
+
import { GuidelineMessage } from './message.js';
|
|
24
19
|
/**
|
|
25
20
|
* Types
|
|
26
21
|
*/
|
|
27
|
-
import type { RequestingStateProp } from '../../types';
|
|
22
|
+
import type { RequestingStateProp } from '../../types.js';
|
|
23
|
+
import type { ReactElement } from 'react';
|
|
28
24
|
type AiControlProps = {
|
|
29
25
|
disabled?: boolean;
|
|
30
26
|
value: string;
|
|
@@ -35,15 +31,15 @@ type AiControlProps = {
|
|
|
35
31
|
isTransparent?: boolean;
|
|
36
32
|
state?: RequestingStateProp;
|
|
37
33
|
showGuideLine?: boolean;
|
|
38
|
-
customFooter?:
|
|
34
|
+
customFooter?: ReactElement;
|
|
39
35
|
onChange?: ( newValue: string ) => void;
|
|
40
36
|
onSend?: ( currentValue: string ) => void;
|
|
41
37
|
onStop?: () => void;
|
|
42
38
|
onAccept?: () => void;
|
|
43
39
|
onDiscard?: () => void;
|
|
44
40
|
showRemove?: boolean;
|
|
45
|
-
bannerComponent?:
|
|
46
|
-
errorComponent?:
|
|
41
|
+
bannerComponent?: ReactElement;
|
|
42
|
+
errorComponent?: ReactElement;
|
|
47
43
|
};
|
|
48
44
|
|
|
49
45
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
@@ -54,7 +50,7 @@ const noop = () => {};
|
|
|
54
50
|
*
|
|
55
51
|
* @param {AiControlProps} props - Component props.
|
|
56
52
|
* @param {React.MutableRefObject} ref - Ref to the component.
|
|
57
|
-
* @returns {
|
|
53
|
+
* @returns {ReactElement} Rendered component.
|
|
58
54
|
*/
|
|
59
55
|
export function AIControl(
|
|
60
56
|
{
|
|
@@ -77,8 +73,8 @@ export function AIControl(
|
|
|
77
73
|
bannerComponent = null,
|
|
78
74
|
errorComponent = null,
|
|
79
75
|
}: AiControlProps,
|
|
80
|
-
ref: React.MutableRefObject< null >
|
|
81
|
-
):
|
|
76
|
+
ref: React.MutableRefObject< null >
|
|
77
|
+
): ReactElement {
|
|
82
78
|
const promptUserInputRef = useRef( null );
|
|
83
79
|
const loading = state === 'requesting' || state === 'suggesting';
|
|
84
80
|
const [ editRequest, setEditRequest ] = React.useState( false );
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import { ExternalLink } from '@wordpress/components';
|
|
4
|
+
import { ExternalLink, Button } from '@wordpress/components';
|
|
5
5
|
import { createInterpolateElement } from '@wordpress/element';
|
|
6
|
-
import { __ } from '@wordpress/i18n';
|
|
6
|
+
import { __, sprintf } from '@wordpress/i18n';
|
|
7
7
|
import {
|
|
8
8
|
Icon,
|
|
9
9
|
warning,
|
|
@@ -84,3 +84,35 @@ export function GuidelineMessage(): React.ReactElement {
|
|
|
84
84
|
</Message>
|
|
85
85
|
);
|
|
86
86
|
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* React component to render a upgrade message.
|
|
90
|
+
*
|
|
91
|
+
* @param {number} requestsRemaining - Number of requests remaining.
|
|
92
|
+
* @returns {React.ReactElement } - Message component.
|
|
93
|
+
*/
|
|
94
|
+
export function UpgradeMessage( {
|
|
95
|
+
requestsRemaining,
|
|
96
|
+
onUpgradeClick,
|
|
97
|
+
}: {
|
|
98
|
+
requestsRemaining: number;
|
|
99
|
+
onUpgradeClick: () => void;
|
|
100
|
+
} ): React.ReactElement {
|
|
101
|
+
return (
|
|
102
|
+
<Message severity={ MESSAGE_SEVERITY_INFO }>
|
|
103
|
+
{ createInterpolateElement(
|
|
104
|
+
sprintf(
|
|
105
|
+
// translators: %1$d: number of requests remaining
|
|
106
|
+
__(
|
|
107
|
+
'You have %1$d free requests remaining. <link>Upgrade</link> and avoid interruptions',
|
|
108
|
+
'jetpack-ai-client'
|
|
109
|
+
),
|
|
110
|
+
requestsRemaining
|
|
111
|
+
),
|
|
112
|
+
{
|
|
113
|
+
link: <Button variant="link" onClick={ onUpgradeClick } />,
|
|
114
|
+
}
|
|
115
|
+
) }
|
|
116
|
+
</Message>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -124,5 +124,11 @@
|
|
|
124
124
|
.components-external-link {
|
|
125
125
|
color: var( --jp-gray-50 );
|
|
126
126
|
}
|
|
127
|
+
|
|
128
|
+
// Force padding 0 in link buttons, since default Gutenberg version in WordPress doesn't use iframe and
|
|
129
|
+
// Buttons receive styles from edit-post-visual-editor.
|
|
130
|
+
.components-button.is-link {
|
|
131
|
+
padding: 0;
|
|
132
|
+
}
|
|
127
133
|
}
|
|
128
134
|
}
|
|
@@ -6,7 +6,7 @@ import classNames from 'classnames';
|
|
|
6
6
|
/*
|
|
7
7
|
* Types
|
|
8
8
|
*/
|
|
9
|
-
import type { RequestingStateProp } from '../../types';
|
|
9
|
+
import type { RequestingStateProp } from '../../types.js';
|
|
10
10
|
export type AiStatusIndicatorIconSize = 24 | 32 | 48 | 64;
|
|
11
11
|
import type React from 'react';
|
|
12
12
|
|
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { useState, useEffect } from '@wordpress/element';
|
|
5
1
|
/*
|
|
6
2
|
* Internal dependencies
|
|
7
3
|
*/
|
|
8
|
-
import { formatTime
|
|
4
|
+
import { formatTime } from './lib/media.js';
|
|
9
5
|
/*
|
|
10
6
|
* Types
|
|
11
7
|
*/
|
|
12
8
|
import type React from 'react';
|
|
13
9
|
|
|
14
10
|
type AudioDurationDisplayProps = {
|
|
15
|
-
|
|
11
|
+
duration: number;
|
|
12
|
+
className?: string | null;
|
|
16
13
|
};
|
|
17
14
|
|
|
18
15
|
/**
|
|
@@ -22,16 +19,8 @@ type AudioDurationDisplayProps = {
|
|
|
22
19
|
* @returns {React.ReactElement} Rendered component.
|
|
23
20
|
*/
|
|
24
21
|
export default function AudioDurationDisplay( {
|
|
25
|
-
|
|
22
|
+
duration,
|
|
23
|
+
className,
|
|
26
24
|
}: AudioDurationDisplayProps ): React.ReactElement {
|
|
27
|
-
|
|
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>;
|
|
25
|
+
return <span className={ className }>{ formatTime( duration, { addDecimalPart: false } ) }</span>;
|
|
37
26
|
}
|
|
@@ -1,67 +1,44 @@
|
|
|
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
1
|
type FormatTimeOptions = {
|
|
33
2
|
/**
|
|
34
3
|
* Whether to add the decimal part to the formatted time.
|
|
35
|
-
*
|
|
36
4
|
*/
|
|
37
5
|
addDecimalPart?: boolean;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Whether to show the minutes part of the formatted time even when it's 0.
|
|
9
|
+
*/
|
|
10
|
+
showMinutes?: boolean;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Whether to show the hours part of the formatted time even when it's 0.
|
|
14
|
+
*/
|
|
15
|
+
showHours?: boolean;
|
|
38
16
|
};
|
|
39
17
|
|
|
40
18
|
/**
|
|
41
19
|
* Formats the given time in milliseconds into a string with the format HH:MM:SS.DD,
|
|
42
20
|
* adding hours and minutes only when needed.
|
|
43
21
|
*
|
|
44
|
-
* @param {number} time - The time in
|
|
22
|
+
* @param {number} time - The time in milliseconds to format.
|
|
45
23
|
* @param {FormatTimeOptions} options - The arguments.
|
|
46
24
|
* @returns {string} The formatted time string.
|
|
47
25
|
* @example
|
|
48
|
-
* const formattedTime1 = formatTime( 1234567 );
|
|
49
|
-
* const formattedTime2 = formatTime( 45123 );
|
|
50
|
-
* const formattedTime3 = formatTime(
|
|
26
|
+
* const formattedTime1 = formatTime( 1234567, { addDecimalPart: true } ); // Returns "20:34.56"
|
|
27
|
+
* const formattedTime2 = formatTime( 45123 ); // Returns "00.45"
|
|
28
|
+
* const formattedTime3 = formatTime( 1200, { showHours: true } ); // Returns "00:00:01"
|
|
51
29
|
*/
|
|
52
30
|
export function formatTime(
|
|
53
31
|
time: number,
|
|
54
|
-
{ addDecimalPart = true }: FormatTimeOptions = {}
|
|
32
|
+
{ addDecimalPart = false, showMinutes = true, showHours = false }: FormatTimeOptions = {}
|
|
55
33
|
): string {
|
|
56
|
-
time = time * 1000;
|
|
57
34
|
const hours = Math.floor( time / 3600000 );
|
|
58
35
|
const minutes = Math.floor( time / 60000 ) % 60;
|
|
59
36
|
const seconds = Math.floor( time / 1000 ) % 60;
|
|
60
37
|
const deciseconds = Math.floor( time / 10 ) % 100;
|
|
61
38
|
|
|
62
39
|
const parts = [
|
|
63
|
-
hours > 0 ? hours.toString().padStart( 2, '0' ) + ':' : '',
|
|
64
|
-
hours > 0 || minutes > 0 ? minutes.toString().padStart( 2, '0' ) + ':' : '',
|
|
40
|
+
hours > 0 || showHours ? hours.toString().padStart( 2, '0' ) + ':' : '',
|
|
41
|
+
hours > 0 || minutes > 0 || showMinutes ? minutes.toString().padStart( 2, '0' ) + ':' : '',
|
|
65
42
|
seconds.toString().padStart( 2, '0' ),
|
|
66
43
|
];
|
|
67
44
|
|
package/src/components/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
export { default as AIControl } from './ai-control';
|
|
2
|
-
export { default as AiStatusIndicator } from './ai-status-indicator';
|
|
3
|
-
export { default as AudioDurationDisplay } from './audio-duration-display';
|
|
4
|
-
export {
|
|
1
|
+
export { default as AIControl } from './ai-control/index.js';
|
|
2
|
+
export { default as AiStatusIndicator } from './ai-status-indicator/index.js';
|
|
3
|
+
export { default as AudioDurationDisplay } from './audio-duration-display/index.js';
|
|
4
|
+
export {
|
|
5
|
+
GuidelineMessage,
|
|
6
|
+
UpgradeMessage,
|
|
7
|
+
default as FooterMessage,
|
|
8
|
+
} from './ai-control/message.js';
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import { createContext } from '
|
|
5
|
-
import React from 'react';
|
|
4
|
+
import React, { createContext } from 'react';
|
|
6
5
|
/**
|
|
7
6
|
* Types & Constants
|
|
8
7
|
*/
|
|
9
|
-
import SuggestionsEventSource from '../suggestions-event-source';
|
|
10
|
-
import type { AskQuestionOptionsArgProps } from '../ask-question';
|
|
11
|
-
import type { RequestingErrorProps } from '../hooks/use-ai-suggestions';
|
|
12
|
-
import type { PromptProp } from '../types';
|
|
13
|
-
import type { RequestingStateProp } from '../types';
|
|
8
|
+
import SuggestionsEventSource from '../suggestions-event-source/index.js';
|
|
9
|
+
import type { AskQuestionOptionsArgProps } from '../ask-question/index.js';
|
|
10
|
+
import type { RequestingErrorProps } from '../hooks/use-ai-suggestions/index.js';
|
|
11
|
+
import type { PromptProp } from '../types.js';
|
|
12
|
+
import type { RequestingStateProp } from '../types.js';
|
|
14
13
|
|
|
15
14
|
export type AiDataContextProps = {
|
|
16
15
|
/*
|
|
@@ -61,7 +60,7 @@ type AiDataContextProviderProps = {
|
|
|
61
60
|
*
|
|
62
61
|
* @returns {AiDataContextProps} Context.
|
|
63
62
|
*/
|
|
64
|
-
export const AiDataContext = createContext( {}
|
|
63
|
+
export const AiDataContext = createContext< AiDataContextProps | object >( {} );
|
|
65
64
|
|
|
66
65
|
/**
|
|
67
66
|
* AI Data Context Provider
|
package/src/data-flow/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { AiDataContext, AiDataContextProvider } from './context';
|
|
2
|
-
export { default as withAiDataProvider } from './with-ai-assistant-data';
|
|
3
|
-
export { default as useAiContext } from './use-ai-context';
|
|
1
|
+
export { AiDataContext, AiDataContextProvider } from './context.js';
|
|
2
|
+
export { default as withAiDataProvider } from './with-ai-assistant-data.js';
|
|
3
|
+
export { default as useAiContext } from './use-ai-context.js';
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import { useCallback, useContext, useEffect } from '
|
|
4
|
+
import { useCallback, useContext, useEffect } from 'react';
|
|
5
5
|
/**
|
|
6
6
|
* Internal dependencies
|
|
7
7
|
*/
|
|
8
|
-
import { ERROR_RESPONSE, RequestingErrorProps } from '../types';
|
|
9
|
-
import { AiDataContext } from '.';
|
|
8
|
+
import { ERROR_RESPONSE, RequestingErrorProps } from '../types.js';
|
|
9
|
+
import { AiDataContext } from './index.js';
|
|
10
10
|
/**
|
|
11
11
|
* Types & constants
|
|
12
12
|
*/
|
|
13
|
-
import type { AiDataContextProps } from './context';
|
|
14
|
-
import type { AskQuestionOptionsArgProps } from '../ask-question';
|
|
13
|
+
import type { AiDataContextProps } from './context.js';
|
|
14
|
+
import type { AskQuestionOptionsArgProps } from '../ask-question/index.js';
|
|
15
15
|
|
|
16
16
|
export type UseAiContextOptions = {
|
|
17
17
|
/*
|
|
@@ -48,7 +48,7 @@ export default function useAiContext( {
|
|
|
48
48
|
onSuggestion,
|
|
49
49
|
onError,
|
|
50
50
|
}: UseAiContextOptions = {} ): AiDataContextProps {
|
|
51
|
-
const context = useContext( AiDataContext );
|
|
51
|
+
const context = useContext( AiDataContext ) as AiDataContextProps;
|
|
52
52
|
const { eventSource } = context;
|
|
53
53
|
|
|
54
54
|
const done = useCallback( ( event: CustomEvent ) => onDone?.( event?.detail ), [ onDone ] );
|
|
@@ -7,8 +7,8 @@ import React from 'react';
|
|
|
7
7
|
/**
|
|
8
8
|
* Internal Dependencies
|
|
9
9
|
*/
|
|
10
|
-
import useAiSuggestions from '../hooks/use-ai-suggestions';
|
|
11
|
-
import { AiDataContextProvider } from '.';
|
|
10
|
+
import useAiSuggestions from '../hooks/use-ai-suggestions/index.js';
|
|
11
|
+
import { AiDataContextProvider } from './index.js';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* High Order Component that provides the
|
|
@@ -7,21 +7,21 @@ import debugFactory from 'debug';
|
|
|
7
7
|
/**
|
|
8
8
|
* Internal dependencies
|
|
9
9
|
*/
|
|
10
|
-
import askQuestion from '../../ask-question';
|
|
10
|
+
import askQuestion from '../../ask-question/index.js';
|
|
11
11
|
import {
|
|
12
12
|
ERROR_MODERATION,
|
|
13
13
|
ERROR_NETWORK,
|
|
14
14
|
ERROR_QUOTA_EXCEEDED,
|
|
15
15
|
ERROR_SERVICE_UNAVAILABLE,
|
|
16
16
|
ERROR_UNCLEAR_PROMPT,
|
|
17
|
-
} from '../../types';
|
|
17
|
+
} from '../../types.js';
|
|
18
18
|
/**
|
|
19
19
|
* Types & constants
|
|
20
20
|
*/
|
|
21
|
-
import type { AskQuestionOptionsArgProps } from '../../ask-question';
|
|
22
|
-
import type SuggestionsEventSource from '../../suggestions-event-source';
|
|
23
|
-
import type { PromptProp, SuggestionErrorCode } from '../../types';
|
|
24
|
-
import type { RequestingStateProp } from '../../types';
|
|
21
|
+
import type { AskQuestionOptionsArgProps } from '../../ask-question/index.js';
|
|
22
|
+
import type SuggestionsEventSource from '../../suggestions-event-source/index.js';
|
|
23
|
+
import type { PromptProp, SuggestionErrorCode } from '../../types.js';
|
|
24
|
+
import type { RequestingStateProp } from '../../types.js';
|
|
25
25
|
|
|
26
26
|
export type RequestingErrorProps = {
|
|
27
27
|
/*
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { useCallback, useState } from '@wordpress/element';
|
|
5
|
+
import debugFactory from 'debug';
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import transcribeAudio from '../../audio-transcription/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Types
|
|
12
|
+
*/
|
|
13
|
+
import type { CancelablePromise } from '../../types.js';
|
|
14
|
+
|
|
15
|
+
const debug = debugFactory( 'jetpack-ai-client:use-audio-transcription' );
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The response from the audio transcription hook.
|
|
19
|
+
*/
|
|
20
|
+
export type UseAudioTranscriptionReturn = {
|
|
21
|
+
transcriptionResult: string;
|
|
22
|
+
isTranscribingAudio: boolean;
|
|
23
|
+
transcriptionError: string;
|
|
24
|
+
transcribeAudio: ( audio: Blob ) => CancelablePromise;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The props for the audio transcription hook.
|
|
29
|
+
*/
|
|
30
|
+
export type UseAudioTranscriptionProps = {
|
|
31
|
+
feature: string;
|
|
32
|
+
onReady?: ( transcription: string ) => void;
|
|
33
|
+
onError?: ( error: string ) => void;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A hook to handle audio transcription.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} feature - The feature name that is calling the transcription.
|
|
40
|
+
* @returns {UseAudioTranscriptionReturn} - Object with properties to get the transcription data.
|
|
41
|
+
*/
|
|
42
|
+
export default function useAudioTranscription( {
|
|
43
|
+
feature,
|
|
44
|
+
onReady,
|
|
45
|
+
onError,
|
|
46
|
+
}: UseAudioTranscriptionProps ): UseAudioTranscriptionReturn {
|
|
47
|
+
const [ transcriptionResult, setTranscriptionResult ] = useState< string >( '' );
|
|
48
|
+
const [ transcriptionError, setTranscriptionError ] = useState< string >( '' );
|
|
49
|
+
const [ isTranscribingAudio, setIsTranscribingAudio ] = useState( false );
|
|
50
|
+
|
|
51
|
+
const handleAudioTranscription = useCallback(
|
|
52
|
+
( audio: Blob ) => {
|
|
53
|
+
debug( 'Transcribing audio' );
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reset the transcription result and error.
|
|
57
|
+
*/
|
|
58
|
+
setTranscriptionResult( '' );
|
|
59
|
+
setTranscriptionError( '' );
|
|
60
|
+
setIsTranscribingAudio( true );
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Call the audio transcription library.
|
|
64
|
+
*/
|
|
65
|
+
const promise: CancelablePromise = transcribeAudio( audio, feature )
|
|
66
|
+
.then( transcriptionText => {
|
|
67
|
+
if ( promise.canceled ) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setTranscriptionResult( transcriptionText );
|
|
72
|
+
onReady?.( transcriptionText );
|
|
73
|
+
} )
|
|
74
|
+
.catch( error => {
|
|
75
|
+
if ( promise.canceled ) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setTranscriptionError( error.message );
|
|
80
|
+
onError?.( error.message );
|
|
81
|
+
} )
|
|
82
|
+
.finally( () => setIsTranscribingAudio( false ) );
|
|
83
|
+
|
|
84
|
+
return promise;
|
|
85
|
+
},
|
|
86
|
+
[ transcribeAudio, setTranscriptionResult, setTranscriptionError, setIsTranscribingAudio ]
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
transcriptionResult,
|
|
91
|
+
isTranscribingAudio,
|
|
92
|
+
transcriptionError,
|
|
93
|
+
transcribeAudio: handleAudioTranscription,
|
|
94
|
+
};
|
|
95
|
+
}
|