@designcrowd/fe-shared-lib 1.6.10-voiceText-2 → 1.6.10-voiceText-4
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/.playwright-cli/page-2026-04-15T06-15-29-589Z.yml +68 -0
- package/.playwright-cli/page-2026-04-15T06-15-41-114Z.yml +68 -0
- package/package.json +1 -1
- package/public/css/tailwind-brandCrowd.css +3 -7
- package/public/css/tailwind-brandPage.css +2 -6
- package/public/css/tailwind-crazyDomains.css +3 -7
- package/public/css/tailwind-designCom.css +3 -7
- package/public/css/tailwind-designCrowd.css +3 -7
- package/src/atoms/components/VoiceToTextButton/VoiceToTextButton.vue +26 -23
- package/src/types/speech-recognition.d.ts +8 -0
- package/src/{useVoiceToText.js → useVoiceToText.ts} +44 -23
- /package/src/atoms/components/VoiceToTextButton/{VoiceToTextButton.stories.js → VoiceToTextButton.stories.ts} +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
- generic [ref=e3]:
|
|
2
|
+
- navigation "Global" [ref=e6]:
|
|
3
|
+
- generic [ref=e10]:
|
|
4
|
+
- generic [ref=e11]:
|
|
5
|
+
- link "Skip to canvas" [ref=e12] [cursor=pointer]:
|
|
6
|
+
- /url: "#storybook-preview-wrapper"
|
|
7
|
+
- link "Storybook" [ref=e14] [cursor=pointer]:
|
|
8
|
+
- /url: ./
|
|
9
|
+
- img "Storybook" [ref=e15]
|
|
10
|
+
- button "Shortcuts" [ref=e21] [cursor=pointer]:
|
|
11
|
+
- img [ref=e22]
|
|
12
|
+
- generic [ref=e25]: Search for components
|
|
13
|
+
- combobox "Search for components" [ref=e27]:
|
|
14
|
+
- generic:
|
|
15
|
+
- img
|
|
16
|
+
- searchbox "Search for components" [ref=e28]
|
|
17
|
+
- code: ⌃ K
|
|
18
|
+
- main [ref=e52]:
|
|
19
|
+
- generic [ref=e54]:
|
|
20
|
+
- generic [ref=e55]:
|
|
21
|
+
- button "Remount component" [ref=e56] [cursor=pointer]:
|
|
22
|
+
- img [ref=e57]
|
|
23
|
+
- button "Zoom in" [ref=e59] [cursor=pointer]:
|
|
24
|
+
- img [ref=e60]
|
|
25
|
+
- button "Zoom out" [ref=e63] [cursor=pointer]:
|
|
26
|
+
- img [ref=e64]
|
|
27
|
+
- button "Reset zoom" [ref=e67] [cursor=pointer]:
|
|
28
|
+
- img [ref=e68]
|
|
29
|
+
- button "Enable measure" [ref=e71] [cursor=pointer]:
|
|
30
|
+
- img [ref=e72]
|
|
31
|
+
- button "Apply outlines to the preview" [ref=e75] [cursor=pointer]:
|
|
32
|
+
- img [ref=e76]
|
|
33
|
+
- button "Change the size of the preview" [ref=e79] [cursor=pointer]:
|
|
34
|
+
- img [ref=e80]
|
|
35
|
+
- button "Vision simulator" [ref=e85] [cursor=pointer]:
|
|
36
|
+
- img [ref=e86]
|
|
37
|
+
- generic:
|
|
38
|
+
- img
|
|
39
|
+
- generic [ref=e90]:
|
|
40
|
+
- button "Go full screen" [ref=e91] [cursor=pointer]:
|
|
41
|
+
- img [ref=e92]
|
|
42
|
+
- link "Open canvas in new tab" [ref=e94] [cursor=pointer]:
|
|
43
|
+
- /url: iframe.html?id=components-voicetotextbutton--side-by-side
|
|
44
|
+
- img [ref=e95]
|
|
45
|
+
- button "Copy canvas link" [ref=e98] [cursor=pointer]:
|
|
46
|
+
- img [ref=e99]
|
|
47
|
+
- generic [ref=e103]:
|
|
48
|
+
- progressbar "Content is loading..." [ref=e105]
|
|
49
|
+
- generic [ref=e106]:
|
|
50
|
+
- link "Skip to sidebar" [ref=e107] [cursor=pointer]:
|
|
51
|
+
- /url: "#components-voicetotextbutton--side-by-side"
|
|
52
|
+
- iframe [ref=e108]:
|
|
53
|
+
|
|
54
|
+
- generic [ref=e113]:
|
|
55
|
+
- tablist [ref=e115]:
|
|
56
|
+
- tab "Controls" [ref=e116] [cursor=pointer]:
|
|
57
|
+
- generic [ref=e118]: Controls
|
|
58
|
+
- tab "Actions" [ref=e119] [cursor=pointer]:
|
|
59
|
+
- generic [ref=e121]: Actions
|
|
60
|
+
- tab "Interactions" [ref=e122] [cursor=pointer]:
|
|
61
|
+
- generic [ref=e124]: Interactions
|
|
62
|
+
- tab "Accessibility" [ref=e125] [cursor=pointer]:
|
|
63
|
+
- generic [ref=e127]: Accessibility
|
|
64
|
+
- generic [ref=e130]:
|
|
65
|
+
- button "Change addon orientation [alt D]" [ref=e131] [cursor=pointer]:
|
|
66
|
+
- img [ref=e132]
|
|
67
|
+
- button "Hide addons [alt A]" [ref=e135] [cursor=pointer]:
|
|
68
|
+
- img [ref=e136]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
- generic [ref=e3]:
|
|
2
|
+
- navigation "Global" [ref=e6]:
|
|
3
|
+
- generic [ref=e10]:
|
|
4
|
+
- generic [ref=e11]:
|
|
5
|
+
- link "Skip to canvas" [ref=e12] [cursor=pointer]:
|
|
6
|
+
- /url: "#storybook-preview-wrapper"
|
|
7
|
+
- link "Storybook" [ref=e14] [cursor=pointer]:
|
|
8
|
+
- /url: ./
|
|
9
|
+
- img "Storybook" [ref=e15]
|
|
10
|
+
- button "Shortcuts" [ref=e21] [cursor=pointer]:
|
|
11
|
+
- img [ref=e22]
|
|
12
|
+
- generic [ref=e25]: Search for components
|
|
13
|
+
- combobox "Search for components" [ref=e27]:
|
|
14
|
+
- generic:
|
|
15
|
+
- img
|
|
16
|
+
- searchbox "Search for components" [ref=e28]
|
|
17
|
+
- code: ⌃ K
|
|
18
|
+
- main [ref=e52]:
|
|
19
|
+
- generic [ref=e54]:
|
|
20
|
+
- generic [ref=e55]:
|
|
21
|
+
- button "Remount component" [ref=e56] [cursor=pointer]:
|
|
22
|
+
- img [ref=e57]
|
|
23
|
+
- button "Zoom in" [ref=e59] [cursor=pointer]:
|
|
24
|
+
- img [ref=e60]
|
|
25
|
+
- button "Zoom out" [ref=e63] [cursor=pointer]:
|
|
26
|
+
- img [ref=e64]
|
|
27
|
+
- button "Reset zoom" [ref=e67] [cursor=pointer]:
|
|
28
|
+
- img [ref=e68]
|
|
29
|
+
- button "Enable measure" [ref=e71] [cursor=pointer]:
|
|
30
|
+
- img [ref=e72]
|
|
31
|
+
- button "Apply outlines to the preview" [ref=e75] [cursor=pointer]:
|
|
32
|
+
- img [ref=e76]
|
|
33
|
+
- button "Change the size of the preview" [ref=e79] [cursor=pointer]:
|
|
34
|
+
- img [ref=e80]
|
|
35
|
+
- button "Vision simulator" [ref=e85] [cursor=pointer]:
|
|
36
|
+
- img [ref=e86]
|
|
37
|
+
- generic:
|
|
38
|
+
- img
|
|
39
|
+
- generic [ref=e90]:
|
|
40
|
+
- button "Go full screen" [ref=e91] [cursor=pointer]:
|
|
41
|
+
- img [ref=e92]
|
|
42
|
+
- link "Open canvas in new tab" [ref=e94] [cursor=pointer]:
|
|
43
|
+
- /url: iframe.html?id=components-voicetotextbutton--prompt-input-demo
|
|
44
|
+
- img [ref=e95]
|
|
45
|
+
- button "Copy canvas link" [ref=e98] [cursor=pointer]:
|
|
46
|
+
- img [ref=e99]
|
|
47
|
+
- generic [ref=e103]:
|
|
48
|
+
- progressbar "Content is loading..." [ref=e105]
|
|
49
|
+
- generic [ref=e106]:
|
|
50
|
+
- link "Skip to sidebar" [ref=e107] [cursor=pointer]:
|
|
51
|
+
- /url: "#components-voicetotextbutton--prompt-input-demo"
|
|
52
|
+
- iframe [ref=e108]:
|
|
53
|
+
|
|
54
|
+
- generic [ref=e113]:
|
|
55
|
+
- tablist [ref=e115]:
|
|
56
|
+
- tab "Controls" [ref=e116] [cursor=pointer]:
|
|
57
|
+
- generic [ref=e118]: Controls
|
|
58
|
+
- tab "Actions" [ref=e119] [cursor=pointer]:
|
|
59
|
+
- generic [ref=e121]: Actions
|
|
60
|
+
- tab "Interactions" [ref=e122] [cursor=pointer]:
|
|
61
|
+
- generic [ref=e124]: Interactions
|
|
62
|
+
- tab "Accessibility" [ref=e125] [cursor=pointer]:
|
|
63
|
+
- generic [ref=e127]: Accessibility
|
|
64
|
+
- generic [ref=e130]:
|
|
65
|
+
- button "Change addon orientation [alt D]" [ref=e131] [cursor=pointer]:
|
|
66
|
+
- img [ref=e132]
|
|
67
|
+
- button "Hide addons [alt A]" [ref=e135] [cursor=pointer]:
|
|
68
|
+
- img [ref=e136]
|
package/package.json
CHANGED
|
@@ -1438,10 +1438,6 @@ video {
|
|
|
1438
1438
|
--tw-bg-opacity: 1;
|
|
1439
1439
|
background-color: rgb(208 208 208 / var(--tw-bg-opacity));
|
|
1440
1440
|
}
|
|
1441
|
-
.theme-brandCrowd .tw-bg-grayscale-700 {
|
|
1442
|
-
--tw-bg-opacity: 1;
|
|
1443
|
-
background-color: rgb(43 43 43 / var(--tw-bg-opacity));
|
|
1444
|
-
}
|
|
1445
1441
|
.theme-brandCrowd .tw-bg-info-100 {
|
|
1446
1442
|
--tw-bg-opacity: 1;
|
|
1447
1443
|
background-color: rgb(204 234 247 / var(--tw-bg-opacity));
|
|
@@ -2257,16 +2253,16 @@ video {
|
|
|
2257
2253
|
outline: 2px solid transparent;
|
|
2258
2254
|
outline-offset: 2px;
|
|
2259
2255
|
}
|
|
2260
|
-
.theme-brandCrowd .focus\:tw-ring-2:focus {
|
|
2256
|
+
.theme-brandCrowd .focus-visible\:tw-ring-2:focus-visible {
|
|
2261
2257
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
2262
2258
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
2263
2259
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
2264
2260
|
}
|
|
2265
|
-
.theme-brandCrowd .focus\:tw-ring-primary-500:focus {
|
|
2261
|
+
.theme-brandCrowd .focus-visible\:tw-ring-primary-500:focus-visible {
|
|
2266
2262
|
--tw-ring-opacity: 1;
|
|
2267
2263
|
--tw-ring-color: rgb(242 27 63 / var(--tw-ring-opacity));
|
|
2268
2264
|
}
|
|
2269
|
-
.theme-brandCrowd .focus\:tw-ring-offset-2:focus {
|
|
2265
|
+
.theme-brandCrowd .focus-visible\:tw-ring-offset-2:focus-visible {
|
|
2270
2266
|
--tw-ring-offset-width: 2px;
|
|
2271
2267
|
}
|
|
2272
2268
|
.theme-brandCrowd .tw-group:hover .group-hover\:tw-text-info-500 {
|
|
@@ -1362,10 +1362,6 @@ video {
|
|
|
1362
1362
|
--tw-bg-opacity: 1;
|
|
1363
1363
|
background-color: rgb(208 208 208 / var(--tw-bg-opacity));
|
|
1364
1364
|
}
|
|
1365
|
-
.theme-brandPage .tw-bg-grayscale-700 {
|
|
1366
|
-
--tw-bg-opacity: 1;
|
|
1367
|
-
background-color: rgb(43 43 43 / var(--tw-bg-opacity));
|
|
1368
|
-
}
|
|
1369
1365
|
.theme-brandPage .tw-bg-transparent {
|
|
1370
1366
|
background-color: transparent;
|
|
1371
1367
|
}
|
|
@@ -1933,12 +1929,12 @@ video {
|
|
|
1933
1929
|
outline: 2px solid transparent;
|
|
1934
1930
|
outline-offset: 2px;
|
|
1935
1931
|
}
|
|
1936
|
-
.theme-brandPage .focus\:tw-ring-2:focus {
|
|
1932
|
+
.theme-brandPage .focus-visible\:tw-ring-2:focus-visible {
|
|
1937
1933
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
1938
1934
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
1939
1935
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
1940
1936
|
}
|
|
1941
|
-
.theme-brandPage .focus\:tw-ring-offset-2:focus {
|
|
1937
|
+
.theme-brandPage .focus-visible\:tw-ring-offset-2:focus-visible {
|
|
1942
1938
|
--tw-ring-offset-width: 2px;
|
|
1943
1939
|
}
|
|
1944
1940
|
@media (min-width: 640px) {
|
|
@@ -1438,10 +1438,6 @@ video {
|
|
|
1438
1438
|
--tw-bg-opacity: 1;
|
|
1439
1439
|
background-color: rgb(199 204 207 / var(--tw-bg-opacity));
|
|
1440
1440
|
}
|
|
1441
|
-
.theme-crazyDomains .tw-bg-grayscale-700 {
|
|
1442
|
-
--tw-bg-opacity: 1;
|
|
1443
|
-
background-color: rgb(87 97 99 / var(--tw-bg-opacity));
|
|
1444
|
-
}
|
|
1445
1441
|
.theme-crazyDomains .tw-bg-info-100 {
|
|
1446
1442
|
--tw-bg-opacity: 1;
|
|
1447
1443
|
background-color: rgb(230 246 253 / var(--tw-bg-opacity));
|
|
@@ -2257,16 +2253,16 @@ video {
|
|
|
2257
2253
|
outline: 2px solid transparent;
|
|
2258
2254
|
outline-offset: 2px;
|
|
2259
2255
|
}
|
|
2260
|
-
.theme-crazyDomains .focus\:tw-ring-2:focus {
|
|
2256
|
+
.theme-crazyDomains .focus-visible\:tw-ring-2:focus-visible {
|
|
2261
2257
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
2262
2258
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
2263
2259
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
2264
2260
|
}
|
|
2265
|
-
.theme-crazyDomains .focus\:tw-ring-primary-500:focus {
|
|
2261
|
+
.theme-crazyDomains .focus-visible\:tw-ring-primary-500:focus-visible {
|
|
2266
2262
|
--tw-ring-opacity: 1;
|
|
2267
2263
|
--tw-ring-color: rgb(111 172 47 / var(--tw-ring-opacity));
|
|
2268
2264
|
}
|
|
2269
|
-
.theme-crazyDomains .focus\:tw-ring-offset-2:focus {
|
|
2265
|
+
.theme-crazyDomains .focus-visible\:tw-ring-offset-2:focus-visible {
|
|
2270
2266
|
--tw-ring-offset-width: 2px;
|
|
2271
2267
|
}
|
|
2272
2268
|
.theme-crazyDomains .tw-group:hover .group-hover\:tw-text-info-500 {
|
|
@@ -1438,10 +1438,6 @@ video {
|
|
|
1438
1438
|
--tw-bg-opacity: 1;
|
|
1439
1439
|
background-color: rgb(209 209 209 / var(--tw-bg-opacity));
|
|
1440
1440
|
}
|
|
1441
|
-
.theme-designCom .tw-bg-grayscale-700 {
|
|
1442
|
-
--tw-bg-opacity: 1;
|
|
1443
|
-
background-color: rgb(38 38 38 / var(--tw-bg-opacity));
|
|
1444
|
-
}
|
|
1445
1441
|
.theme-designCom .tw-bg-info-100 {
|
|
1446
1442
|
--tw-bg-opacity: 1;
|
|
1447
1443
|
background-color: rgb(236 238 254 / var(--tw-bg-opacity));
|
|
@@ -2257,16 +2253,16 @@ video {
|
|
|
2257
2253
|
outline: 2px solid transparent;
|
|
2258
2254
|
outline-offset: 2px;
|
|
2259
2255
|
}
|
|
2260
|
-
.theme-designCom .focus\:tw-ring-2:focus {
|
|
2256
|
+
.theme-designCom .focus-visible\:tw-ring-2:focus-visible {
|
|
2261
2257
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
2262
2258
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
2263
2259
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
2264
2260
|
}
|
|
2265
|
-
.theme-designCom .focus\:tw-ring-primary-500:focus {
|
|
2261
|
+
.theme-designCom .focus-visible\:tw-ring-primary-500:focus-visible {
|
|
2266
2262
|
--tw-ring-opacity: 1;
|
|
2267
2263
|
--tw-ring-color: rgb(63 89 246 / var(--tw-ring-opacity));
|
|
2268
2264
|
}
|
|
2269
|
-
.theme-designCom .focus\:tw-ring-offset-2:focus {
|
|
2265
|
+
.theme-designCom .focus-visible\:tw-ring-offset-2:focus-visible {
|
|
2270
2266
|
--tw-ring-offset-width: 2px;
|
|
2271
2267
|
}
|
|
2272
2268
|
.theme-designCom .tw-group:hover .group-hover\:tw-text-info-500 {
|
|
@@ -1438,10 +1438,6 @@ video {
|
|
|
1438
1438
|
--tw-bg-opacity: 1;
|
|
1439
1439
|
background-color: rgb(204 204 204 / var(--tw-bg-opacity));
|
|
1440
1440
|
}
|
|
1441
|
-
.theme-designCrowd .tw-bg-grayscale-700 {
|
|
1442
|
-
--tw-bg-opacity: 1;
|
|
1443
|
-
background-color: rgb(82 93 96 / var(--tw-bg-opacity));
|
|
1444
|
-
}
|
|
1445
1441
|
.theme-designCrowd .tw-bg-info-100 {
|
|
1446
1442
|
--tw-bg-opacity: 1;
|
|
1447
1443
|
background-color: rgb(207 234 251 / var(--tw-bg-opacity));
|
|
@@ -2257,16 +2253,16 @@ video {
|
|
|
2257
2253
|
outline: 2px solid transparent;
|
|
2258
2254
|
outline-offset: 2px;
|
|
2259
2255
|
}
|
|
2260
|
-
.theme-designCrowd .focus\:tw-ring-2:focus {
|
|
2256
|
+
.theme-designCrowd .focus-visible\:tw-ring-2:focus-visible {
|
|
2261
2257
|
--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
|
|
2262
2258
|
--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
|
|
2263
2259
|
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
|
|
2264
2260
|
}
|
|
2265
|
-
.theme-designCrowd .focus\:tw-ring-primary-500:focus {
|
|
2261
|
+
.theme-designCrowd .focus-visible\:tw-ring-primary-500:focus-visible {
|
|
2266
2262
|
--tw-ring-opacity: 1;
|
|
2267
2263
|
--tw-ring-color: rgb(17 151 235 / var(--tw-ring-opacity));
|
|
2268
2264
|
}
|
|
2269
|
-
.theme-designCrowd .focus\:tw-ring-offset-2:focus {
|
|
2265
|
+
.theme-designCrowd .focus-visible\:tw-ring-offset-2:focus-visible {
|
|
2270
2266
|
--tw-ring-offset-width: 2px;
|
|
2271
2267
|
}
|
|
2272
2268
|
.theme-designCrowd .tw-group:hover .group-hover\:tw-text-info-500 {
|
|
@@ -19,38 +19,35 @@
|
|
|
19
19
|
name="microphone"
|
|
20
20
|
:size="iconSize"
|
|
21
21
|
:class="variantClasses.icon"
|
|
22
|
+
aria-hidden="true"
|
|
22
23
|
/>
|
|
23
24
|
</button>
|
|
24
25
|
</template>
|
|
25
26
|
|
|
26
|
-
<script setup>
|
|
27
|
+
<script setup lang="ts">
|
|
27
28
|
import { watch, toRef, computed } from 'vue';
|
|
28
29
|
import Icon from '../Icon/Icon.vue';
|
|
29
30
|
import { useVoiceToText } from '../../../useVoiceToText';
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
disabled
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
variant:
|
|
46
|
-
type: String,
|
|
47
|
-
default: 'dark',
|
|
48
|
-
validator: (value) => ['light', 'dark'].includes(value),
|
|
49
|
-
},
|
|
32
|
+
type ButtonSize = 'sm' | 'md' | 'lg';
|
|
33
|
+
type ButtonVariant = 'light' | 'dark';
|
|
34
|
+
|
|
35
|
+
interface VoiceToTextButtonProps {
|
|
36
|
+
lang?: string;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
size?: ButtonSize;
|
|
39
|
+
variant?: ButtonVariant;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const props = withDefaults(defineProps<VoiceToTextButtonProps>(), {
|
|
43
|
+
lang: 'en-US',
|
|
44
|
+
disabled: false,
|
|
45
|
+
size: 'md',
|
|
46
|
+
variant: 'dark',
|
|
50
47
|
});
|
|
51
48
|
|
|
52
49
|
const sizeClasses = computed(() => {
|
|
53
|
-
const sizes = {
|
|
50
|
+
const sizes: Record<ButtonSize, string> = {
|
|
54
51
|
sm: 'tw-w-8 tw-h-8',
|
|
55
52
|
md: 'tw-w-10 tw-h-10',
|
|
56
53
|
lg: 'tw-w-12 tw-h-12',
|
|
@@ -59,7 +56,7 @@ const sizeClasses = computed(() => {
|
|
|
59
56
|
});
|
|
60
57
|
|
|
61
58
|
const iconSize = computed(() => {
|
|
62
|
-
const sizes = {
|
|
59
|
+
const sizes: Record<ButtonSize, string> = {
|
|
63
60
|
sm: 'sm',
|
|
64
61
|
md: 'md',
|
|
65
62
|
lg: 'md',
|
|
@@ -67,7 +64,13 @@ const iconSize = computed(() => {
|
|
|
67
64
|
return sizes[props.size];
|
|
68
65
|
});
|
|
69
66
|
|
|
70
|
-
const emit = defineEmits
|
|
67
|
+
const emit = defineEmits<{
|
|
68
|
+
'on-transcript': [transcript: string];
|
|
69
|
+
'on-interim-transcript': [transcript: string];
|
|
70
|
+
'on-start': [];
|
|
71
|
+
'on-stop': [];
|
|
72
|
+
'on-error': [error: string];
|
|
73
|
+
}>();
|
|
71
74
|
|
|
72
75
|
const { isSupported, isListening, transcript, isFinal, error, toggle, setLang } = useVoiceToText({
|
|
73
76
|
lang: props.lang,
|
|
@@ -1,13 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable no-undef */
|
|
2
|
+
import { ref, computed, readonly, type Ref, type ComputedRef, type DeepReadonly } from 'vue';
|
|
3
|
+
|
|
4
|
+
export interface UseVoiceToTextOptions {
|
|
5
|
+
lang?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface UseVoiceToTextReturn {
|
|
9
|
+
isSupported: ComputedRef<boolean>;
|
|
10
|
+
isListening: DeepReadonly<Ref<boolean>>;
|
|
11
|
+
transcript: DeepReadonly<Ref<string>>;
|
|
12
|
+
isFinal: DeepReadonly<Ref<boolean>>;
|
|
13
|
+
error: DeepReadonly<Ref<string | null>>;
|
|
14
|
+
start: () => void;
|
|
15
|
+
stop: () => void;
|
|
16
|
+
toggle: () => void;
|
|
17
|
+
// eslint-disable-next-line no-unused-vars
|
|
18
|
+
setLang: (lang: string) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface VoiceToTextState {
|
|
22
|
+
isListening: Ref<boolean>;
|
|
23
|
+
transcript: Ref<string>;
|
|
24
|
+
isFinal: Ref<boolean>;
|
|
25
|
+
error: Ref<string | null>;
|
|
26
|
+
}
|
|
2
27
|
|
|
3
28
|
// Singleton instance and state (lazily initialized)
|
|
4
|
-
let recognition = null;
|
|
29
|
+
let recognition: SpeechRecognition | null = null;
|
|
5
30
|
let isInitialized = false;
|
|
6
|
-
let errorClearTimeout = null;
|
|
7
|
-
let state = null;
|
|
31
|
+
let errorClearTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
32
|
+
let state: VoiceToTextState | null = null;
|
|
8
33
|
|
|
9
34
|
// Error message mapping per spec
|
|
10
|
-
const ERROR_MESSAGES = {
|
|
35
|
+
const ERROR_MESSAGES: Record<string, string> = {
|
|
11
36
|
'not-allowed': 'Microphone permission was denied. Please allow access.',
|
|
12
37
|
'language-not-supported': 'This language is not supported.',
|
|
13
38
|
network: 'A network error occurred. Please check your connection.',
|
|
@@ -16,13 +41,13 @@ const ERROR_MESSAGES = {
|
|
|
16
41
|
|
|
17
42
|
const ERROR_CLEAR_DELAY = 5000;
|
|
18
43
|
|
|
19
|
-
function getState() {
|
|
44
|
+
function getState(): VoiceToTextState {
|
|
20
45
|
if (!state) {
|
|
21
46
|
state = {
|
|
22
47
|
isListening: ref(false),
|
|
23
48
|
transcript: ref(''),
|
|
24
49
|
isFinal: ref(false),
|
|
25
|
-
error: ref(null),
|
|
50
|
+
error: ref<string | null>(null),
|
|
26
51
|
};
|
|
27
52
|
}
|
|
28
53
|
return state;
|
|
@@ -31,30 +56,26 @@ function getState() {
|
|
|
31
56
|
/**
|
|
32
57
|
* Singleton composable that wraps the Web Speech API (SpeechRecognition).
|
|
33
58
|
* All calls to useVoiceToText() return the same shared instance.
|
|
34
|
-
*
|
|
35
|
-
* @param {Object} options
|
|
36
|
-
* @param {string} options.lang - BCP 47 language tag (default: 'en-US')
|
|
37
|
-
* @returns {Object} Voice-to-text state and controls
|
|
38
59
|
*/
|
|
39
|
-
export function useVoiceToText(options = {}) {
|
|
60
|
+
export function useVoiceToText(options: UseVoiceToTextOptions = {}): UseVoiceToTextReturn {
|
|
40
61
|
const { lang = 'en-US' } = options;
|
|
41
62
|
|
|
42
63
|
// Get or create shared state
|
|
43
64
|
const { isListening, transcript, isFinal, error } = getState();
|
|
44
65
|
|
|
45
66
|
// Check for browser support
|
|
46
|
-
const SpeechRecognition =
|
|
67
|
+
const SpeechRecognitionCtor: typeof SpeechRecognition | null =
|
|
47
68
|
typeof window !== 'undefined' ? window.SpeechRecognition || window.webkitSpeechRecognition : null;
|
|
48
69
|
|
|
49
|
-
const isSupported = computed(() => !!
|
|
70
|
+
const isSupported = computed(() => !!SpeechRecognitionCtor);
|
|
50
71
|
|
|
51
72
|
// Initialize singleton once
|
|
52
|
-
if (!isInitialized &&
|
|
53
|
-
recognition = new
|
|
73
|
+
if (!isInitialized && SpeechRecognitionCtor) {
|
|
74
|
+
recognition = new SpeechRecognitionCtor();
|
|
54
75
|
recognition.continuous = true;
|
|
55
76
|
recognition.interimResults = true;
|
|
56
77
|
|
|
57
|
-
recognition.onresult = (event) => {
|
|
78
|
+
recognition.onresult = (event: SpeechRecognitionEvent) => {
|
|
58
79
|
let interimTranscript = '';
|
|
59
80
|
let finalTranscript = '';
|
|
60
81
|
|
|
@@ -76,7 +97,7 @@ export function useVoiceToText(options = {}) {
|
|
|
76
97
|
}
|
|
77
98
|
};
|
|
78
99
|
|
|
79
|
-
recognition.onerror = (event) => {
|
|
100
|
+
recognition.onerror = (event: SpeechRecognitionErrorEvent) => {
|
|
80
101
|
// Suppress no-speech and aborted errors per spec
|
|
81
102
|
if (event.error === 'no-speech' || event.error === 'aborted') {
|
|
82
103
|
return;
|
|
@@ -124,10 +145,10 @@ export function useVoiceToText(options = {}) {
|
|
|
124
145
|
try {
|
|
125
146
|
recognition.start();
|
|
126
147
|
isListening.value = true;
|
|
127
|
-
} catch (e) {
|
|
148
|
+
} catch (e: unknown) {
|
|
128
149
|
// Handle case where recognition is already started
|
|
129
150
|
// eslint-disable-next-line no-console
|
|
130
|
-
console.warn('[useVoiceToText] Failed to start:', e.message);
|
|
151
|
+
console.warn('[useVoiceToText] Failed to start:', (e as Error).message);
|
|
131
152
|
}
|
|
132
153
|
};
|
|
133
154
|
|
|
@@ -137,10 +158,10 @@ export function useVoiceToText(options = {}) {
|
|
|
137
158
|
isListening.value = false;
|
|
138
159
|
try {
|
|
139
160
|
recognition.stop();
|
|
140
|
-
} catch (e) {
|
|
161
|
+
} catch (e: unknown) {
|
|
141
162
|
// Handle case where recognition is already stopped
|
|
142
163
|
// eslint-disable-next-line no-console
|
|
143
|
-
console.warn('[useVoiceToText] Failed to stop:', e.message);
|
|
164
|
+
console.warn('[useVoiceToText] Failed to stop:', (e as Error).message);
|
|
144
165
|
}
|
|
145
166
|
};
|
|
146
167
|
|
|
@@ -152,7 +173,7 @@ export function useVoiceToText(options = {}) {
|
|
|
152
173
|
}
|
|
153
174
|
};
|
|
154
175
|
|
|
155
|
-
const setLang = (newLang) => {
|
|
176
|
+
const setLang = (newLang: string) => {
|
|
156
177
|
if (recognition) {
|
|
157
178
|
recognition.lang = newLang;
|
|
158
179
|
}
|
|
File without changes
|