@designcrowd/fe-shared-lib 1.8.3 → 1.8.4-edge-fallback-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.
Files changed (29) hide show
  1. package/.playwright-cli/page-2026-05-05T01-37-40-892Z.yml +68 -0
  2. package/.playwright-cli/page-2026-05-05T01-37-44-367Z.yml +448 -0
  3. package/.playwright-cli/page-2026-05-05T01-37-54-274Z.yml +445 -0
  4. package/.playwright-cli/page-2026-05-05T01-38-02-746Z.yml +448 -0
  5. package/.playwright-cli/page-2026-05-05T01-38-11-519Z.yml +68 -0
  6. package/.playwright-cli/page-2026-05-05T01-38-30-060Z.yml +440 -0
  7. package/.playwright-cli/page-2026-05-05T01-38-42-137Z.yml +68 -0
  8. package/.playwright-cli/page-2026-05-05T01-38-45-269Z.yml +445 -0
  9. package/.playwright-cli/page-2026-05-05T01-43-09-147Z.yml +68 -0
  10. package/.playwright-cli/page-2026-05-05T01-43-14-323Z.yml +448 -0
  11. package/.playwright-cli/page-2026-05-05T01-43-31-765Z.png +0 -0
  12. package/.playwright-cli/page-2026-05-05T01-43-41-236Z.yml +445 -0
  13. package/.playwright-cli/page-2026-05-05T01-43-45-996Z.png +0 -0
  14. package/.playwright-cli/page-2026-05-05T01-43-55-336Z.yml +448 -0
  15. package/.playwright-cli/page-2026-05-05T01-43-59-714Z.png +0 -0
  16. package/.playwright-cli/page-2026-05-05T01-44-09-967Z.yml +445 -0
  17. package/.playwright-cli/page-2026-05-05T01-44-11-884Z.yml +68 -0
  18. package/.playwright-cli/page-2026-05-05T01-44-18-229Z.yml +440 -0
  19. package/.playwright-cli/page-2026-05-05T01-44-54-928Z.png +0 -0
  20. package/.playwright-cli/page-2026-05-05T01-45-20-437Z.yml +0 -0
  21. package/.playwright-cli/page-2026-05-05T01-45-23-521Z.yml +438 -0
  22. package/.playwright-cli/page-2026-05-05T01-45-39-082Z.yml +438 -0
  23. package/.playwright-cli/page-2026-05-05T01-45-55-158Z.yml +435 -0
  24. package/.playwright-cli/page-2026-05-05T01-46-09-528Z.png +0 -0
  25. package/force-unsupported-hidden.png +0 -0
  26. package/force-unsupported-with-button.png +0 -0
  27. package/package.json +1 -1
  28. package/src/atoms/components/VoiceToTextButton/VoiceToTextButton.stories.ts +68 -0
  29. package/src/useVoiceToText.ts +58 -1
@@ -0,0 +1,435 @@
1
+ - generic [ref=e3]:
2
+ - navigation "Global" [ref=e6]:
3
+ - generic [ref=e9]:
4
+ - generic [ref=e10]:
5
+ - generic [ref=e11]:
6
+ - link "Skip to canvas" [ref=e12] [cursor=pointer]:
7
+ - /url: "#storybook-preview-wrapper"
8
+ - link "Storybook" [ref=e14] [cursor=pointer]:
9
+ - /url: ./
10
+ - img "Storybook" [ref=e15]
11
+ - button "Shortcuts" [ref=e21] [cursor=pointer]:
12
+ - img [ref=e22]
13
+ - generic [ref=e25]: Search for components
14
+ - combobox "Search for components" [ref=e27]:
15
+ - generic:
16
+ - img
17
+ - searchbox "Search for components" [ref=e28]
18
+ - code: ⌃ K
19
+ - button "Tag filters" [ref=e32] [cursor=pointer]:
20
+ - img [ref=e33]
21
+ - generic [ref=e39]:
22
+ - img
23
+ - generic [ref=e40]:
24
+ - button "Components" [expanded] [ref=e41] [cursor=pointer]:
25
+ - img [ref=e43]
26
+ - text: Components
27
+ - button "Collapse" [ref=e45] [cursor=pointer]:
28
+ - img [ref=e46]
29
+ - button "AiPoweredLoader" [ref=e49] [cursor=pointer]:
30
+ - generic [ref=e50]:
31
+ - img [ref=e52]
32
+ - img [ref=e54]
33
+ - text: AiPoweredLoader
34
+ - button "Buttons" [ref=e57] [cursor=pointer]:
35
+ - generic [ref=e58]:
36
+ - img [ref=e60]
37
+ - img [ref=e62]
38
+ - text: Buttons
39
+ - button "ButtonGroup" [ref=e65] [cursor=pointer]:
40
+ - generic [ref=e66]:
41
+ - img [ref=e68]
42
+ - img [ref=e70]
43
+ - text: ButtonGroup
44
+ - button "Carousel" [ref=e73] [cursor=pointer]:
45
+ - generic [ref=e74]:
46
+ - img [ref=e76]
47
+ - img [ref=e78]
48
+ - text: Carousel
49
+ - button "Checkbox" [ref=e81] [cursor=pointer]:
50
+ - generic [ref=e82]:
51
+ - img [ref=e84]
52
+ - img [ref=e86]
53
+ - text: Checkbox
54
+ - button "Checktile" [ref=e89] [cursor=pointer]:
55
+ - generic [ref=e90]:
56
+ - img [ref=e92]
57
+ - img [ref=e94]
58
+ - text: Checktile
59
+ - button "CollapsiblePanel" [ref=e97] [cursor=pointer]:
60
+ - generic [ref=e98]:
61
+ - img [ref=e100]
62
+ - img [ref=e102]
63
+ - text: CollapsiblePanel
64
+ - button "Color Picker" [ref=e105] [cursor=pointer]:
65
+ - generic [ref=e106]:
66
+ - img [ref=e108]
67
+ - img [ref=e110]
68
+ - text: Color Picker
69
+ - button "CopyToClipboardText" [ref=e113] [cursor=pointer]:
70
+ - generic [ref=e114]:
71
+ - img [ref=e116]
72
+ - img [ref=e118]
73
+ - text: CopyToClipboardText
74
+ - button "Dropdown" [ref=e121] [cursor=pointer]:
75
+ - generic [ref=e122]:
76
+ - img [ref=e124]
77
+ - img [ref=e126]
78
+ - text: Dropdown
79
+ - button "HelloBar" [ref=e129] [cursor=pointer]:
80
+ - generic [ref=e130]:
81
+ - img [ref=e132]
82
+ - img [ref=e134]
83
+ - text: HelloBar
84
+ - button "Icons" [ref=e137] [cursor=pointer]:
85
+ - generic [ref=e138]:
86
+ - img [ref=e140]
87
+ - img [ref=e142]
88
+ - text: Icons
89
+ - button "Masonry" [ref=e145] [cursor=pointer]:
90
+ - generic [ref=e146]:
91
+ - img [ref=e148]
92
+ - img [ref=e150]
93
+ - text: Masonry
94
+ - button "Hash Router Modal" [ref=e153] [cursor=pointer]:
95
+ - generic [ref=e154]:
96
+ - img [ref=e156]
97
+ - img [ref=e158]
98
+ - text: Hash Router Modal
99
+ - button "Modal" [ref=e161] [cursor=pointer]:
100
+ - generic [ref=e162]:
101
+ - img [ref=e164]
102
+ - img [ref=e166]
103
+ - text: Modal
104
+ - button "Notice" [ref=e169] [cursor=pointer]:
105
+ - generic [ref=e170]:
106
+ - img [ref=e172]
107
+ - img [ref=e174]
108
+ - text: Notice
109
+ - button "Number Stepper" [ref=e177] [cursor=pointer]:
110
+ - generic [ref=e178]:
111
+ - img [ref=e180]
112
+ - img [ref=e182]
113
+ - text: Number Stepper
114
+ - button "Picture" [ref=e185] [cursor=pointer]:
115
+ - generic [ref=e186]:
116
+ - img [ref=e188]
117
+ - img [ref=e190]
118
+ - text: Picture
119
+ - button "Pill" [ref=e193] [cursor=pointer]:
120
+ - generic [ref=e194]:
121
+ - img [ref=e196]
122
+ - img [ref=e198]
123
+ - text: Pill
124
+ - button "PillBar" [ref=e201] [cursor=pointer]:
125
+ - generic [ref=e202]:
126
+ - img [ref=e204]
127
+ - img [ref=e206]
128
+ - text: PillBar
129
+ - button "Price" [ref=e209] [cursor=pointer]:
130
+ - generic [ref=e210]:
131
+ - img [ref=e212]
132
+ - img [ref=e214]
133
+ - text: Price
134
+ - button "PromoCard" [ref=e217] [cursor=pointer]:
135
+ - generic [ref=e218]:
136
+ - img [ref=e220]
137
+ - img [ref=e222]
138
+ - text: PromoCard
139
+ - button "Search Bar" [ref=e225] [cursor=pointer]:
140
+ - generic [ref=e226]:
141
+ - img [ref=e228]
142
+ - img [ref=e230]
143
+ - text: Search Bar
144
+ - button "Select" [ref=e233] [cursor=pointer]:
145
+ - generic [ref=e234]:
146
+ - img [ref=e236]
147
+ - img [ref=e238]
148
+ - text: Select
149
+ - button "Slider" [ref=e241] [cursor=pointer]:
150
+ - generic [ref=e242]:
151
+ - img [ref=e244]
152
+ - img [ref=e246]
153
+ - text: Slider
154
+ - button "SparkleIcon" [ref=e249] [cursor=pointer]:
155
+ - generic [ref=e250]:
156
+ - img [ref=e252]
157
+ - img [ref=e254]
158
+ - text: SparkleIcon
159
+ - button "Star Rating" [ref=e257] [cursor=pointer]:
160
+ - generic [ref=e258]:
161
+ - img [ref=e260]
162
+ - img [ref=e262]
163
+ - text: Star Rating
164
+ - button "Tab Menu" [ref=e265] [cursor=pointer]:
165
+ - generic [ref=e266]:
166
+ - img [ref=e268]
167
+ - img [ref=e270]
168
+ - text: Tab Menu
169
+ - button "Text Copy Field" [ref=e273] [cursor=pointer]:
170
+ - generic [ref=e274]:
171
+ - img [ref=e276]
172
+ - img [ref=e278]
173
+ - text: Text Copy Field
174
+ - button "Text Input" [ref=e281] [cursor=pointer]:
175
+ - generic [ref=e282]:
176
+ - img [ref=e284]
177
+ - img [ref=e286]
178
+ - text: Text Input
179
+ - button "Textarea" [ref=e289] [cursor=pointer]:
180
+ - generic [ref=e290]:
181
+ - img [ref=e292]
182
+ - img [ref=e294]
183
+ - text: Textarea
184
+ - button "Toggle" [ref=e297] [cursor=pointer]:
185
+ - generic [ref=e298]:
186
+ - img [ref=e300]
187
+ - img [ref=e302]
188
+ - text: Toggle
189
+ - button "Tooltip" [ref=e305] [cursor=pointer]:
190
+ - generic [ref=e306]:
191
+ - img [ref=e308]
192
+ - img [ref=e310]
193
+ - text: Tooltip
194
+ - button "VoiceToTextButton" [expanded] [ref=e313] [cursor=pointer]:
195
+ - generic [ref=e314]:
196
+ - img [ref=e316]
197
+ - img [ref=e318]
198
+ - text: VoiceToTextButton
199
+ - link "Prompt Input Demo" [ref=e321] [cursor=pointer]:
200
+ - /url: /?path=/story/components-voicetotextbutton--prompt-input-demo
201
+ - img [ref=e323]
202
+ - text: Prompt Input Demo
203
+ - link "Dark Variant States" [ref=e326] [cursor=pointer]:
204
+ - /url: /?path=/story/components-voicetotextbutton--dark-variant-states
205
+ - img [ref=e328]
206
+ - text: Dark Variant States
207
+ - generic [ref=e330]:
208
+ - link "Light Input Demo" [ref=e331] [cursor=pointer]:
209
+ - /url: /?path=/story/components-voicetotextbutton--light-input-demo
210
+ - img [ref=e333]
211
+ - text: Light Input Demo
212
+ - link "Skip to canvas" [ref=e335] [cursor=pointer]:
213
+ - /url: "#storybook-preview-wrapper"
214
+ - link "Light Variant States" [ref=e337] [cursor=pointer]:
215
+ - /url: /?path=/story/components-voicetotextbutton--light-variant-states
216
+ - img [ref=e339]
217
+ - text: Light Variant States
218
+ - link "Side By Side" [ref=e342] [cursor=pointer]:
219
+ - /url: /?path=/story/components-voicetotextbutton--side-by-side
220
+ - img [ref=e344]
221
+ - text: Side By Side
222
+ - link "Force Unsupported" [ref=e347] [cursor=pointer]:
223
+ - /url: /?path=/story/components-voicetotextbutton--force-unsupported
224
+ - img [ref=e349]
225
+ - text: Force Unsupported
226
+ - button "DesignCom" [ref=e352] [cursor=pointer]:
227
+ - generic [ref=e353]:
228
+ - img [ref=e355]
229
+ - img [ref=e357]
230
+ - text: DesignCom
231
+ - button "SignIn" [ref=e360] [cursor=pointer]:
232
+ - generic [ref=e361]:
233
+ - img [ref=e363]
234
+ - img [ref=e365]
235
+ - text: SignIn
236
+ - button "PaymentConfigList" [ref=e368] [cursor=pointer]:
237
+ - generic [ref=e369]:
238
+ - img [ref=e371]
239
+ - img [ref=e373]
240
+ - text: PaymentConfigList
241
+ - button "PublishBrandPageModal" [ref=e376] [cursor=pointer]:
242
+ - generic [ref=e377]:
243
+ - img [ref=e379]
244
+ - img [ref=e381]
245
+ - text: PublishBrandPageModal
246
+ - button "SellDomainNameListModal" [ref=e384] [cursor=pointer]:
247
+ - generic [ref=e385]:
248
+ - img [ref=e387]
249
+ - img [ref=e389]
250
+ - text: SellDomainNameListModal
251
+ - button "SellDomainNameSearchWithResults" [ref=e392] [cursor=pointer]:
252
+ - generic [ref=e393]:
253
+ - img [ref=e395]
254
+ - img [ref=e397]
255
+ - text: SellDomainNameSearchWithResults
256
+ - button "SellDomainNameWidget" [ref=e400] [cursor=pointer]:
257
+ - generic [ref=e401]:
258
+ - img [ref=e403]
259
+ - img [ref=e405]
260
+ - text: SellDomainNameWidget
261
+ - button "UploadYourLogoApplication" [ref=e408] [cursor=pointer]:
262
+ - generic [ref=e409]:
263
+ - img [ref=e411]
264
+ - img [ref=e413]
265
+ - text: UploadYourLogoApplication
266
+ - button "UploadYourLogoDropzone" [ref=e416] [cursor=pointer]:
267
+ - generic [ref=e417]:
268
+ - img [ref=e419]
269
+ - img [ref=e421]
270
+ - text: UploadYourLogoDropzone
271
+ - button "LogoBusinessBrandColours" [ref=e424] [cursor=pointer]:
272
+ - generic [ref=e425]:
273
+ - img [ref=e427]
274
+ - img [ref=e429]
275
+ - text: LogoBusinessBrandColours
276
+ - button "LogoKeywords" [ref=e432] [cursor=pointer]:
277
+ - generic [ref=e433]:
278
+ - img [ref=e435]
279
+ - img [ref=e437]
280
+ - text: LogoKeywords
281
+ - button "UploadedLogoSearchResultCard" [ref=e440] [cursor=pointer]:
282
+ - generic [ref=e441]:
283
+ - img [ref=e443]
284
+ - img [ref=e445]
285
+ - text: UploadedLogoSearchResultCard
286
+ - button "WebsitesContextualUpgradeModal" [ref=e448] [cursor=pointer]:
287
+ - generic [ref=e449]:
288
+ - img [ref=e451]
289
+ - img [ref=e453]
290
+ - text: WebsitesContextualUpgradeModal
291
+ - generic [ref=e455]:
292
+ - button "Experiences" [expanded] [ref=e456] [cursor=pointer]:
293
+ - img [ref=e458]
294
+ - text: Experiences
295
+ - button "Collapse" [ref=e460] [cursor=pointer]:
296
+ - img [ref=e461]
297
+ - button "SideNavigationPanel" [ref=e464] [cursor=pointer]:
298
+ - generic [ref=e465]:
299
+ - img [ref=e467]
300
+ - img [ref=e469]
301
+ - text: SideNavigationPanel
302
+ - link "Storybook 10.3 Learn what's new in Storybook" [ref=e473] [cursor=pointer]:
303
+ - /url: /?path=/settings/whats-new
304
+ - img [ref=e475]
305
+ - generic [ref=e477]:
306
+ - generic "Storybook 10.3" [ref=e478]
307
+ - generic [ref=e479]: Learn what's new in Storybook
308
+ - button "Dismiss notification" [ref=e480]:
309
+ - img [ref=e481]
310
+ - main [ref=e484]:
311
+ - generic [ref=e486]:
312
+ - generic [ref=e487]:
313
+ - button "Remount component" [ref=e488] [cursor=pointer]:
314
+ - img [ref=e489]
315
+ - button "Zoom in" [ref=e491] [cursor=pointer]:
316
+ - img [ref=e492]
317
+ - button "Zoom out" [ref=e495] [cursor=pointer]:
318
+ - img [ref=e496]
319
+ - button "Reset zoom" [ref=e499] [cursor=pointer]:
320
+ - img [ref=e500]
321
+ - button "Apply a grid to the preview" [ref=e503] [cursor=pointer]:
322
+ - img [ref=e504]
323
+ - button "Change the background of the preview" [ref=e507] [cursor=pointer]:
324
+ - img [ref=e508]
325
+ - button "Enable measure" [ref=e511] [cursor=pointer]:
326
+ - img [ref=e512]
327
+ - button "Apply outlines to the preview" [ref=e515] [cursor=pointer]:
328
+ - img [ref=e516]
329
+ - button "Change the size of the preview" [ref=e519] [cursor=pointer]:
330
+ - img [ref=e520]
331
+ - button "Vision simulator" [ref=e525] [cursor=pointer]:
332
+ - img [ref=e526]
333
+ - generic:
334
+ - img
335
+ - button "brandCrowd theme" [ref=e531] [cursor=pointer]:
336
+ - img [ref=e532]
337
+ - generic [ref=e534]: brandCrowd theme
338
+ - button "🇺🇸 English (US)" [ref=e537] [cursor=pointer]:
339
+ - img [ref=e538]
340
+ - text: 🇺🇸 English (US)
341
+ - generic [ref=e540]:
342
+ - button "Go full screen" [ref=e541] [cursor=pointer]:
343
+ - img [ref=e542]
344
+ - link "Open canvas in new tab" [ref=e544] [cursor=pointer]:
345
+ - /url: iframe.html?globals=&id=components-voicetotextbutton--light-input-demo
346
+ - img [ref=e545]
347
+ - button "Copy canvas link" [ref=e548] [cursor=pointer]:
348
+ - img [ref=e549]
349
+ - generic [ref=e554]:
350
+ - link "Skip to sidebar" [ref=e555] [cursor=pointer]:
351
+ - /url: "#components-voicetotextbutton--light-input-demo"
352
+ - iframe [ref=e556]:
353
+ - generic [active] [ref=f1e1]:
354
+ - generic [ref=f1e3]:
355
+ - heading "Search" [level=1] [ref=f1e4]
356
+ - paragraph [ref=f1e5]: Find what you're looking for
357
+ - textbox "Type to search..." [ref=f1e7]
358
+ - generic [ref=f1e8]:
359
+ - generic "Toggle devtools panel" [ref=f1e9] [cursor=pointer]:
360
+ - img [ref=f1e10]
361
+ - generic "Toggle Component Inspector" [ref=f1e15] [cursor=pointer]:
362
+ - img [ref=f1e16]
363
+ - generic [ref=e559]:
364
+ - generic [ref=e561]:
365
+ - tablist [ref=e563]:
366
+ - tab "Controls 5" [ref=e564] [cursor=pointer]:
367
+ - generic [ref=e565]:
368
+ - generic [ref=e566]: Controls
369
+ - generic [ref=e567]: "5"
370
+ - tab "Actions" [ref=e568] [cursor=pointer]:
371
+ - generic [ref=e570]: Actions
372
+ - tab "Interactions" [ref=e571] [cursor=pointer]:
373
+ - generic [ref=e573]: Interactions
374
+ - tab "Accessibility 1" [ref=e574] [cursor=pointer]:
375
+ - generic [ref=e575]:
376
+ - generic [ref=e576]: Accessibility
377
+ - generic [ref=e577]: "1"
378
+ - generic [ref=e580]:
379
+ - button "Change addon orientation [alt D]" [ref=e581] [cursor=pointer]:
380
+ - img [ref=e582]
381
+ - button "Hide addons [alt A]" [ref=e585] [cursor=pointer]:
382
+ - img [ref=e586]
383
+ - table [ref=e593]:
384
+ - rowgroup [ref=e594]:
385
+ - row "Name Control" [ref=e595]:
386
+ - columnheader "Name" [ref=e596]
387
+ - columnheader "Control" [ref=e597]:
388
+ - generic [ref=e598]:
389
+ - text: Control
390
+ - button "Reset controls" [ref=e599] [cursor=pointer]:
391
+ - img [ref=e600]
392
+ - rowgroup [ref=e602]:
393
+ - row "Hide props items props Hide props items" [ref=e603]:
394
+ - cell "Hide props items props" [ref=e604]:
395
+ - button "Hide props items" [ref=e605]
396
+ - generic [ref=e606]:
397
+ - img [ref=e607]
398
+ - text: props
399
+ - cell "Hide props items" [ref=e609]:
400
+ - button "Hide props items" [ref=e610]
401
+ - row "lang -" [ref=e611]:
402
+ - cell "lang" [ref=e612]
403
+ - cell "-" [ref=e613]
404
+ - row "disabled -" [ref=e614]:
405
+ - cell "disabled" [ref=e615]
406
+ - cell "-" [ref=e616]
407
+ - row "size -" [ref=e617]:
408
+ - cell "size" [ref=e618]
409
+ - cell "-" [ref=e619]
410
+ - row "variant -" [ref=e620]:
411
+ - cell "variant" [ref=e621]
412
+ - cell "-" [ref=e622]
413
+ - row "Hide events items events Hide events items" [ref=e623]:
414
+ - cell "Hide events items events" [ref=e624]:
415
+ - button "Hide events items" [ref=e625]
416
+ - generic [ref=e626]:
417
+ - img [ref=e627]
418
+ - text: events
419
+ - cell "Hide events items" [ref=e629]:
420
+ - button "Hide events items" [ref=e630]
421
+ - row "on-transcript -" [ref=e631]:
422
+ - cell "on-transcript" [ref=e632]
423
+ - cell "-" [ref=e633]
424
+ - row "on-interim-transcript -" [ref=e634]:
425
+ - cell "on-interim-transcript" [ref=e635]
426
+ - cell "-" [ref=e636]
427
+ - row "on-start -" [ref=e637]:
428
+ - cell "on-start" [ref=e638]
429
+ - cell "-" [ref=e639]
430
+ - row "on-stop -" [ref=e640]:
431
+ - cell "on-stop" [ref=e641]
432
+ - cell "-" [ref=e642]
433
+ - row "on-error -" [ref=e643]:
434
+ - cell "on-error" [ref=e644]
435
+ - cell "-" [ref=e645]
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@designcrowd/fe-shared-lib",
3
- "version": "1.8.3",
3
+ "version": "1.8.4-edge-fallback-0",
4
4
  "scripts": {
5
5
  "start": "run-p storybook watch:translation",
6
6
  "build": "npm run build:css --production",
@@ -1,4 +1,5 @@
1
1
  import VoiceToTextButton from './VoiceToTextButton.vue';
2
+ import { __setVoiceToTextSessionDisabled } from '../../../useVoiceToText';
2
3
 
3
4
  export default {
4
5
  title: 'Components/VoiceToTextButton',
@@ -240,3 +241,70 @@ export const SideBySide = () => ({
240
241
  SideBySide.story = {
241
242
  name: 'Side by Side Comparison',
242
243
  };
244
+
245
+ export const ForceUnsupported = () => ({
246
+ components: { VoiceToTextButton },
247
+ data() {
248
+ return {
249
+ // Reactive ping so the button re-renders when we flip the session flag.
250
+ // The composable's isSupported is reactive on its own, but the parent
251
+ // template uses :key to make the toggle obvious in Storybook.
252
+ tick: 0,
253
+ };
254
+ },
255
+ methods: {
256
+ forceUnsupported() {
257
+ __setVoiceToTextSessionDisabled(true);
258
+ this.tick += 1;
259
+ },
260
+ reset() {
261
+ __setVoiceToTextSessionDisabled(false);
262
+ this.tick += 1;
263
+ },
264
+ },
265
+ template: `
266
+ <div class="tw-min-h-[400px] tw-p-8 tw-flex tw-flex-col tw-items-center tw-justify-center tw-bg-white">
267
+ <h3 class="tw-text-grayscale-800 tw-text-lg tw-font-semibold tw-mb-2">Force unsupported preview</h3>
268
+ <p class="tw-text-grayscale-600 tw-text-sm tw-mb-6 tw-text-center tw-max-w-md">
269
+ Simulates the Edge fallback: after a 'network' SpeechRecognitionError, the composable latches
270
+ <code class="tw-bg-grayscale-100 tw-px-1 tw-rounded">isSupported</code> to false for the rest of the session
271
+ and the button hides itself. Reset clears the session flag.
272
+ </p>
273
+
274
+ <div class="tw-flex tw-gap-3 tw-mb-6">
275
+ <button
276
+ type="button"
277
+ @click="forceUnsupported"
278
+ class="tw-px-4 tw-py-2 tw-rounded tw-bg-error-500 tw-text-white tw-text-sm hover:tw-bg-error-600"
279
+ >
280
+ Force unsupported
281
+ </button>
282
+ <button
283
+ type="button"
284
+ @click="reset"
285
+ class="tw-px-4 tw-py-2 tw-rounded tw-bg-grayscale-200 tw-text-grayscale-800 tw-text-sm hover:tw-bg-grayscale-300"
286
+ >
287
+ Reset
288
+ </button>
289
+ </div>
290
+
291
+ <div class="tw-w-full tw-max-w-xl tw-bg-grayscale-100 tw-rounded-full tw-px-6 tw-py-3 tw-flex tw-items-center tw-gap-3 tw-border tw-border-grayscale-300 tw-min-h-[64px]">
292
+ <input
293
+ type="text"
294
+ placeholder="Voice input would appear on the right..."
295
+ class="tw-flex-1 tw-bg-transparent tw-border-none tw-text-grayscale-800 tw-placeholder-grayscale-500 focus:tw-outline-none tw-text-base"
296
+ />
297
+ <VoiceToTextButton :key="tick" variant="light" size="md" />
298
+ </div>
299
+
300
+ <p class="tw-text-grayscale-500 tw-text-xs tw-mt-4">
301
+ Tip: open DevTools → Application → Session Storage to see the
302
+ <code class="tw-bg-grayscale-100 tw-px-1 tw-rounded">fe-shared-lib:voice-to-text-disabled</code> key.
303
+ </p>
304
+ </div>
305
+ `,
306
+ });
307
+
308
+ ForceUnsupported.story = {
309
+ name: 'Force Unsupported (Edge Fallback)',
310
+ };
@@ -30,6 +30,14 @@ let recognition: SpeechRecognition | null = null;
30
30
  let isInitialized = false;
31
31
  let errorClearTimeout: ReturnType<typeof setTimeout> | null = null;
32
32
  let state: VoiceToTextState | null = null;
33
+ let sessionDisabled: Ref<boolean> | null = null;
34
+
35
+ // Edge ships window.SpeechRecognition but routes it through Microsoft's Online
36
+ // Speech service, which is gated by a Windows privacy toggle that's commonly
37
+ // off — start() then fires a 'network' error. There's no synchronous capability
38
+ // check, so we latch on the first network error and treat the feature as
39
+ // unsupported for the rest of the session. See issue #161.
40
+ const SESSION_DISABLED_KEY = 'fe-shared-lib:voice-to-text-disabled';
33
41
 
34
42
  // Error message mapping per spec
35
43
  const ERROR_MESSAGES: Record<string, string> = {
@@ -53,6 +61,43 @@ function getState(): VoiceToTextState {
53
61
  return state;
54
62
  }
55
63
 
64
+ function getSessionDisabled(): Ref<boolean> {
65
+ if (!sessionDisabled) {
66
+ let initial = false;
67
+ try {
68
+ initial = typeof window !== 'undefined' && window.sessionStorage?.getItem(SESSION_DISABLED_KEY) === '1';
69
+ } catch {
70
+ // sessionStorage can throw in sandboxed iframes / disabled-cookie contexts
71
+ }
72
+ sessionDisabled = ref(initial);
73
+ }
74
+ return sessionDisabled;
75
+ }
76
+
77
+ function setSessionDisabled(disabled: boolean): void {
78
+ const flag = getSessionDisabled();
79
+ flag.value = disabled;
80
+ try {
81
+ if (typeof window === 'undefined') return;
82
+ if (disabled) {
83
+ window.sessionStorage?.setItem(SESSION_DISABLED_KEY, '1');
84
+ } else {
85
+ window.sessionStorage?.removeItem(SESSION_DISABLED_KEY);
86
+ }
87
+ } catch {
88
+ // ignore storage failures — in-memory flag still applies for this session
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Test/Storybook helper: force the session-disabled latch on or off without
94
+ * needing a real network error. Not part of the public consumer API.
95
+ */
96
+ // eslint-disable-next-line no-underscore-dangle
97
+ export function __setVoiceToTextSessionDisabled(disabled: boolean): void {
98
+ setSessionDisabled(disabled);
99
+ }
100
+
56
101
  /**
57
102
  * Singleton composable that wraps the Web Speech API (SpeechRecognition).
58
103
  * All calls to useVoiceToText() return the same shared instance.
@@ -67,7 +112,8 @@ export function useVoiceToText(options: UseVoiceToTextOptions = {}): UseVoiceToT
67
112
  const SpeechRecognitionCtor: typeof SpeechRecognition | null =
68
113
  typeof window !== 'undefined' ? window.SpeechRecognition || window.webkitSpeechRecognition : null;
69
114
 
70
- const isSupported = computed(() => !!SpeechRecognitionCtor);
115
+ const sessionDisabledRef = getSessionDisabled();
116
+ const isSupported = computed(() => !!SpeechRecognitionCtor && !sessionDisabledRef.value);
71
117
 
72
118
  // Initialize singleton once
73
119
  if (!isInitialized && SpeechRecognitionCtor) {
@@ -98,6 +144,17 @@ export function useVoiceToText(options: UseVoiceToTextOptions = {}): UseVoiceToT
98
144
  return;
99
145
  }
100
146
 
147
+ // Latch on first network error: Edge exposes SpeechRecognition but the
148
+ // backend doesn't actually work for most users. Hide the feature for the
149
+ // rest of the session rather than surfacing a recurring toast.
150
+ if (event.error === 'network') {
151
+ // eslint-disable-next-line no-console
152
+ console.warn('[useVoiceToText] network error — disabling voice input for this session');
153
+ isListening.value = false;
154
+ setSessionDisabled(true);
155
+ return;
156
+ }
157
+
101
158
  const message = ERROR_MESSAGES[event.error] || 'An error occurred with speech recognition.';
102
159
  error.value = message;
103
160