@askly/widget 2.10.0 → 2.11.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/README.md +49 -2
- package/dist/core/events.d.ts +19 -0
- package/dist/core/speech.d.ts +43 -0
- package/dist/core/types.d.ts +3 -0
- package/dist/icons.d.ts +0 -3
- package/dist/index.d.ts +7 -0
- package/dist/index.esm.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/ui/Composer.d.ts +11 -9
- package/dist/ui/VoiceRecorder.d.ts +17 -0
- package/dist/widget.d.ts +1 -1
- package/dist/widget.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ Askly is an **embeddable AI support chat widget** for React and plain HTML. Drop
|
|
|
13
13
|
|
|
14
14
|
- 🤖 **AI-powered answers** — resolves customer questions automatically from your knowledge base.
|
|
15
15
|
- 📚 **RAG (docs-grounded)** — replies grounded in your own documentation, not hallucinated.
|
|
16
|
-
- 🎙️ **Voice
|
|
16
|
+
- 🎙️ **Voice input** — customers can speak their message; it is transcribed and sent as text.
|
|
17
17
|
- 🙋 **Human handoff** — one-click escalation to a live agent when needed.
|
|
18
18
|
- ⚛️ **React + CDN** — use as an npm module, or a single `<script>` tag on any site (no React required).
|
|
19
19
|
- 🎨 **Fully themeable** — colors, logo, position, and copy configured in the portal.
|
|
@@ -179,6 +179,48 @@ or a self-hosted server older than this feature) the widget falls back to checki
|
|
|
179
179
|
seconds while the chat is open, as before. If your site sets a Content Security Policy, the Askly
|
|
180
180
|
API origin must be in `connect-src`.
|
|
181
181
|
|
|
182
|
+
### Voice input
|
|
183
|
+
|
|
184
|
+
Visitors can press the microphone button, speak, and have their words turned into a message. The
|
|
185
|
+
recording stops when they press done, after about two seconds of silence, or at 60 seconds;
|
|
186
|
+
Escape cancels it. It works in current Chrome, Edge, Safari and Firefox.
|
|
187
|
+
|
|
188
|
+
The clip is sent to the Askly server, transcribed there in the language spoken, and not stored.
|
|
189
|
+
The browser's own speech recognition is not used, so the audio goes to Askly and nowhere else.
|
|
190
|
+
|
|
191
|
+
| Mode | What happens to the transcript |
|
|
192
|
+
| :--- | :--- |
|
|
193
|
+
| `"review"` | It is placed in the message box for the visitor to check, edit and send. |
|
|
194
|
+
| `"auto-send"` | It is sent straight away. |
|
|
195
|
+
| off | No microphone button. |
|
|
196
|
+
|
|
197
|
+
Set the mode in the portal under **Widget**. An embed can narrow it, never widen it:
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
Askly.init({ appId: "…", voiceInput: "auto-send" }); // or "review", or false to turn it off here
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
```html
|
|
204
|
+
<script src="…/widget.js" data-app-id="…" data-voice-input="review" async></script>
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Each organization has a monthly allowance of transcription minutes, shown in the portal. When it
|
|
208
|
+
is used up, or a visitor records too many clips in a short time, the visitor is asked to type
|
|
209
|
+
instead.
|
|
210
|
+
|
|
211
|
+
Requirements on the host page:
|
|
212
|
+
|
|
213
|
+
- It must be served over **https** (browsers refuse the microphone otherwise).
|
|
214
|
+
- If you send a `Permissions-Policy` header, it must allow `microphone` for your own origin, and
|
|
215
|
+
an iframe that contains the widget needs `allow="microphone"`.
|
|
216
|
+
- With a Content Security Policy, the Askly API origin must be in `connect-src`.
|
|
217
|
+
|
|
218
|
+
`enableVoiceChat` is the older on/off switch: `false` still turns voice input off for the embed,
|
|
219
|
+
`true` leaves the choice to the portal. Voice input needs a server that offers transcription; with
|
|
220
|
+
an older self-hosted server the microphone button is not shown. The
|
|
221
|
+
`readAloud` option, which spoke replies with the browser's speech synthesis, was removed in
|
|
222
|
+
2.11.0 and is ignored.
|
|
223
|
+
|
|
182
224
|
### Asking visitors for their email
|
|
183
225
|
|
|
184
226
|
Anonymous visitors can't be replied to once they close the tab — the answer just waits in a widget
|
|
@@ -243,7 +285,8 @@ straight after `Askly.init()` are held and carried out once the widget has mount
|
|
|
243
285
|
| `Askly.showNewMessage(text?)` | Open the chat with the message box focused, optionally pre-filled. Nothing is sent. |
|
|
244
286
|
| `Askly.sendMessage(text)` | Open the chat and send `text` as the visitor. It passes the same checks as a typed message; if one is pending (email required, security check) it waits in the message box. |
|
|
245
287
|
| `Askly.showArticle(id)` | Open a help article by id. |
|
|
246
|
-
| `Askly.
|
|
288
|
+
| `Askly.startVoiceInput()` / `Askly.stopVoiceInput()` | Open the chat and start recording a voice message; finish the recording and transcribe it. The browser may ask the visitor for the microphone. |
|
|
289
|
+
| `Askly.update(config)` | Change display settings without re-initialising, e.g. `{ themeColor, theme, organizationName, welcomeMessage, buttonShape }`. `voiceInput` can be narrowed here too. |
|
|
247
290
|
| `Askly.isOpen()` | `true` while the panel is open. |
|
|
248
291
|
| `Askly.getUnreadCount()` | Agent replies the visitor has not seen (the launcher badge). |
|
|
249
292
|
| `Askly.getVisitorId()` | This browser's anonymous visitor id (`""` before mount). |
|
|
@@ -272,6 +315,10 @@ affect the widget.
|
|
|
272
315
|
| `lead:captured` | none | The visitor left an email address. |
|
|
273
316
|
| `identify:success` / `identify:error` | `{ userId, status? }` | The server accepted or refused `identify()`. |
|
|
274
317
|
| `greeting:shown` / `greeting:clicked` / `greeting:dismissed` | none | Greeting bubble activity. |
|
|
318
|
+
| `voice:start` | none | Voice input started recording. |
|
|
319
|
+
| `voice:stop` | `{ durationMs }` | Recording ended and the clip is being transcribed. |
|
|
320
|
+
| `voice:transcript` | `{ text, language? }` | The transcript arrived, before it is placed in the message box or sent. |
|
|
321
|
+
| `voice:error` | `{ code }` | Voice input failed: `insecure`, `unsupported`, `denied`, `no-microphone`, `no-speech`, `no-audio`, `limit`, `disabled`, `network` or `failed`. |
|
|
275
322
|
| `error` | `{ message, status? }` | A message could not be sent. |
|
|
276
323
|
|
|
277
324
|
```javascript
|
package/dist/core/events.d.ts
CHANGED
|
@@ -45,6 +45,21 @@ export interface AsklyEventMap {
|
|
|
45
45
|
"greeting:shown": undefined;
|
|
46
46
|
"greeting:clicked": undefined;
|
|
47
47
|
"greeting:dismissed": undefined;
|
|
48
|
+
/** Voice input started recording. */
|
|
49
|
+
"voice:start": undefined;
|
|
50
|
+
/** Recording ended; the clip is about to be transcribed. */
|
|
51
|
+
"voice:stop": {
|
|
52
|
+
durationMs: number;
|
|
53
|
+
};
|
|
54
|
+
/** The transcript, before it is placed in the message box or sent. */
|
|
55
|
+
"voice:transcript": {
|
|
56
|
+
text: string;
|
|
57
|
+
language?: string;
|
|
58
|
+
};
|
|
59
|
+
/** Voice input failed: insecure, unsupported, denied, no-microphone, no-speech, no-audio, limit, disabled, network, failed. */
|
|
60
|
+
"voice:error": {
|
|
61
|
+
code: string;
|
|
62
|
+
};
|
|
48
63
|
/** A request failed in a way the visitor was told about. */
|
|
49
64
|
error: {
|
|
50
65
|
message: string;
|
|
@@ -76,6 +91,10 @@ export type AsklyCommand = {
|
|
|
76
91
|
} | {
|
|
77
92
|
type: "showArticle";
|
|
78
93
|
articleId: string;
|
|
94
|
+
} | {
|
|
95
|
+
type: "startVoiceInput";
|
|
96
|
+
} | {
|
|
97
|
+
type: "stopVoiceInput";
|
|
79
98
|
} | {
|
|
80
99
|
type: "update";
|
|
81
100
|
config: Record<string, any>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Microphone capture for voice input.
|
|
3
|
+
*
|
|
4
|
+
* Records one short clip with MediaRecorder; the widget posts it to the Askly server, which
|
|
5
|
+
* returns the transcript. Nothing here touches the browser's own speech recognition, so the
|
|
6
|
+
* audio goes to Askly and nowhere else.
|
|
7
|
+
*/
|
|
8
|
+
export type VoiceInputMode = "off" | "review" | "auto-send";
|
|
9
|
+
/** Why voice input could not start or finish. */
|
|
10
|
+
export type VoiceErrorCode = "insecure" | "unsupported" | "denied" | "no-microphone" | "no-speech" | "no-audio" | "limit" | "disabled" | "network" | "failed";
|
|
11
|
+
export interface VoiceClip {
|
|
12
|
+
blob: Blob;
|
|
13
|
+
durationMs: number;
|
|
14
|
+
/**
|
|
15
|
+
* "no-speech": recording gave up because nobody spoke. "no-audio": the microphone delivered
|
|
16
|
+
* silence throughout. Otherwise "ok", which includes every clip the visitor ended themselves:
|
|
17
|
+
* whether it contains words is then for the transcription to say, not for a level meter.
|
|
18
|
+
*/
|
|
19
|
+
outcome: "ok" | "no-speech" | "no-audio";
|
|
20
|
+
}
|
|
21
|
+
export interface Recording {
|
|
22
|
+
/** Finish and hand the clip to `onDone`. */
|
|
23
|
+
stop(): void;
|
|
24
|
+
/** Throw the clip away. `onDone` is not called. */
|
|
25
|
+
cancel(): void;
|
|
26
|
+
}
|
|
27
|
+
export interface RecordingOptions {
|
|
28
|
+
maxMs: number;
|
|
29
|
+
/** Stop this long after the speaker goes quiet. */
|
|
30
|
+
silenceMs: number;
|
|
31
|
+
/** Stop when nothing has been said for this long from the start. */
|
|
32
|
+
noSpeechMs: number;
|
|
33
|
+
/** About ten times a second while recording. `level` is 0 to 1. */
|
|
34
|
+
onMeter: (level: number, elapsedMs: number) => void;
|
|
35
|
+
onDone: (clip: VoiceClip) => void;
|
|
36
|
+
}
|
|
37
|
+
export declare function normalizeVoiceInputMode(value: any): VoiceInputMode | undefined;
|
|
38
|
+
/** Why recording cannot work on this page, or null when it can. */
|
|
39
|
+
export declare function voiceInputBlocker(): VoiceErrorCode | null;
|
|
40
|
+
/** Ask for the microphone and start recording. Rejects with `{ code: VoiceErrorCode }`. */
|
|
41
|
+
export declare function startRecording(options: RecordingOptions): Promise<Recording>;
|
|
42
|
+
/** What to tell the visitor. */
|
|
43
|
+
export declare function voiceErrorMessage(code: VoiceErrorCode): string;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -7,7 +7,10 @@ export interface ChatWidgetProps {
|
|
|
7
7
|
theme?: "light" | "dark" | "auto";
|
|
8
8
|
position?: "left" | "right";
|
|
9
9
|
orgServerRoute: string;
|
|
10
|
+
voiceInput?: false | "review" | "auto-send";
|
|
11
|
+
/** @deprecated use `voiceInput`. `false` turns voice input off for this embed. */
|
|
10
12
|
enableVoiceChat?: boolean;
|
|
13
|
+
/** @deprecated removed in 2.11.0; ignored. */
|
|
11
14
|
readAloud?: boolean;
|
|
12
15
|
welcomeMessage?: string;
|
|
13
16
|
placeholderText?: string;
|
package/dist/icons.d.ts
CHANGED
|
@@ -8,9 +8,6 @@ export declare const X: ({ size, color, strokeWidth, ...rest }: IconProps) => Re
|
|
|
8
8
|
export declare const Minus: ({ size, color, strokeWidth, ...rest }: IconProps) => React.ReactSVGElement;
|
|
9
9
|
export declare const ArrowUp: ({ size, color, strokeWidth, ...rest }: IconProps) => React.ReactSVGElement;
|
|
10
10
|
export declare const Mic: ({ size, color, strokeWidth, ...rest }: IconProps) => React.ReactSVGElement;
|
|
11
|
-
export declare const MicOff: ({ size, color, strokeWidth, ...rest }: IconProps) => React.ReactSVGElement;
|
|
12
|
-
export declare const Volume2: ({ size, color, strokeWidth, ...rest }: IconProps) => React.ReactSVGElement;
|
|
13
|
-
export declare const VolumeX: ({ size, color, strokeWidth, ...rest }: IconProps) => React.ReactSVGElement;
|
|
14
11
|
export declare const Home: ({ size, color, strokeWidth, ...rest }: IconProps) => React.ReactSVGElement;
|
|
15
12
|
export declare const Compass: ({ size, color, strokeWidth, ...rest }: IconProps) => React.ReactSVGElement;
|
|
16
13
|
export declare const MessageSquare: ({ size, color, strokeWidth, ...rest }: IconProps) => React.ReactSVGElement;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,10 @@ export interface AsklyConfig {
|
|
|
9
9
|
position?: "left" | "right";
|
|
10
10
|
themeColor?: string;
|
|
11
11
|
theme?: "light" | "dark" | "auto";
|
|
12
|
+
voiceInput?: false | "review" | "auto-send";
|
|
13
|
+
/** @deprecated use `voiceInput`. */
|
|
12
14
|
enableVoiceChat?: boolean;
|
|
15
|
+
/** @deprecated removed in 2.11.0; ignored. */
|
|
13
16
|
readAloud?: boolean;
|
|
14
17
|
welcomeMessage?: string;
|
|
15
18
|
placeholderText?: string;
|
|
@@ -107,6 +110,10 @@ declare const Askly: {
|
|
|
107
110
|
sendMessage: (text: string) => void;
|
|
108
111
|
/** Open a help article by id, as listed in the Help tab. */
|
|
109
112
|
showArticle: (articleId: string) => void;
|
|
113
|
+
/** Open the chat and start recording a voice message. The browser may ask for the microphone. */
|
|
114
|
+
startVoiceInput: () => void;
|
|
115
|
+
/** Finish the recording in progress and transcribe it. */
|
|
116
|
+
stopVoiceInput: () => void;
|
|
110
117
|
/**
|
|
111
118
|
* Change display settings on the mounted widget without re-initialising: the fields the
|
|
112
119
|
* portal controls, e.g. `{ themeColor, theme, organizationName, welcomeMessage, buttonShape }`.
|