@designcrowd/fe-shared-lib 1.6.10-voiceText → 1.6.10-voiceText-2

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 (51) hide show
  1. package/.claude/settings.local.json +4 -9
  2. package/.claude/skills/playwright-cli/SKILL.md +278 -0
  3. package/.claude/skills/playwright-cli/references/request-mocking.md +87 -0
  4. package/.claude/skills/playwright-cli/references/running-code.md +232 -0
  5. package/.claude/skills/playwright-cli/references/session-management.md +169 -0
  6. package/.claude/skills/playwright-cli/references/storage-state.md +275 -0
  7. package/.claude/skills/playwright-cli/references/test-generation.md +88 -0
  8. package/.claude/skills/playwright-cli/references/tracing.md +139 -0
  9. package/.claude/skills/playwright-cli/references/video-recording.md +43 -0
  10. package/.playwright-cli/page-2026-04-15T02-26-54-483Z.yml +68 -0
  11. package/.playwright-cli/page-2026-04-15T02-27-08-312Z.yml +0 -0
  12. package/.playwright-cli/page-2026-04-15T02-27-25-596Z.yml +0 -0
  13. package/.playwright-cli/page-2026-04-15T02-27-40-203Z.yml +0 -0
  14. package/.playwright-cli/page-2026-04-15T02-38-58-180Z.yml +0 -0
  15. package/.playwright-cli/page-2026-04-15T02-39-01-736Z.yml +26 -0
  16. package/.playwright-cli/page-2026-04-15T02-39-10-233Z.yml +26 -0
  17. package/.playwright-cli/page-2026-04-15T02-39-43-909Z.yml +26 -0
  18. package/.playwright-cli/page-2026-04-15T02-40-44-800Z.yml +0 -0
  19. package/.playwright-cli/page-2026-04-15T02-40-54-188Z.yml +26 -0
  20. package/.playwright-cli/page-2026-04-15T02-40-59-031Z.yml +26 -0
  21. package/.playwright-cli/page-2026-04-15T02-51-07-111Z.yml +0 -0
  22. package/.playwright-cli/page-2026-04-15T02-51-10-941Z.yml +26 -0
  23. package/.playwright-cli/page-2026-04-15T02-51-17-020Z.yml +26 -0
  24. package/.playwright-cli/page-2026-04-15T02-51-42-403Z.yml +2 -0
  25. package/.playwright-cli/page-2026-04-15T02-51-53-552Z.yml +26 -0
  26. package/.playwright-cli/page-2026-04-15T02-51-54-631Z.yml +26 -0
  27. package/.playwright-cli/page-2026-04-15T02-52-16-170Z.yml +26 -0
  28. package/.playwright-cli/page-2026-04-15T02-52-17-246Z.yml +26 -0
  29. package/.playwright-cli/page-2026-04-15T02-52-28-472Z.yml +26 -0
  30. package/.playwright-cli/page-2026-04-15T02-53-15-507Z.yml +0 -0
  31. package/.playwright-cli/page-2026-04-15T02-53-16-554Z.yml +26 -0
  32. package/.playwright-cli/page-2026-04-15T02-53-22-178Z.yml +26 -0
  33. package/.playwright-cli/page-2026-04-15T02-53-34-973Z.yml +26 -0
  34. package/CLAUDE.md +35 -0
  35. package/docs/voice-to-text-discussion-attachments/Screenshot 2026-04-15 at 11.44.18/342/200/257am.png +0 -0
  36. package/docs/voice-to-text-discussion-attachments/image.png +0 -0
  37. package/docs/voice-to-text-discussion-attachments/image_1.png +0 -0
  38. package/docs/voice-to-text-discussion-attachments/image_2.png +0 -0
  39. package/docs/voice-to-text-discussion-attachments/image_3.png +0 -0
  40. package/docs/voice-to-text-discussion-attachments/image_4.png +0 -0
  41. package/docs/voice-to-text-discussion.md +330 -0
  42. package/package.json +3 -1
  43. package/public/css/tailwind-brandCrowd.css +29 -0
  44. package/public/css/tailwind-brandPage.css +25 -0
  45. package/public/css/tailwind-crazyDomains.css +29 -0
  46. package/public/css/tailwind-designCom.css +29 -0
  47. package/public/css/tailwind-designCrowd.css +29 -0
  48. package/src/atoms/components/Modal/Modal.vue +9 -0
  49. package/src/atoms/components/VoiceToTextButton/VoiceToTextButton.stories.js +185 -50
  50. package/src/atoms/components/VoiceToTextButton/VoiceToTextButton.vue +73 -13
  51. package/src/useVoiceToText.js +1 -0
@@ -1,18 +1,12 @@
1
1
  import VoiceToTextButton from './VoiceToTextButton.vue';
2
- import TextInput from '../TextInput/TextInput.vue';
3
2
 
4
3
  export default {
5
4
  title: 'Components/VoiceToTextButton',
6
5
  component: VoiceToTextButton,
7
6
  };
8
7
 
9
- export const Default = () => ({
8
+ export const PromptInputDemo = () => ({
10
9
  components: { VoiceToTextButton },
11
- template: '<VoiceToTextButton />',
12
- });
13
-
14
- export const WithTranscriptDisplay = () => ({
15
- components: { VoiceToTextButton, TextInput },
16
10
  data() {
17
11
  return {
18
12
  transcript: '',
@@ -21,87 +15,228 @@ export const WithTranscriptDisplay = () => ({
21
15
  };
22
16
  },
23
17
  template: `
24
- <div class="tw-flex tw-flex-col tw-gap-4 tw-max-w-md">
25
- <div class="tw-flex tw-gap-2 tw-items-end">
26
- <TextInput
18
+ <div class="tw-min-h-[400px] tw-p-8 tw-flex tw-flex-col tw-items-center tw-justify-center" style="background: linear-gradient(180deg, #1a1a2e 0%, #16213e 100%);">
19
+ <h1 class="tw-text-white tw-text-3xl tw-font-bold tw-mb-2 tw-text-center">Design anything with AI</h1>
20
+ <p class="tw-text-grayscale-400 tw-mb-8 tw-text-center">Design a logo, a website, or anything else in seconds with the power of AI</p>
21
+
22
+ <div class="tw-w-full tw-max-w-xl tw-bg-grayscale-800 tw-rounded-full tw-px-6 tw-py-3 tw-flex tw-items-center tw-gap-3">
23
+ <input
27
24
  v-model="transcript"
28
- label="Search"
29
- placeholder="Click the mic and speak..."
30
- class="tw-flex-1"
25
+ type="text"
26
+ placeholder="What would you like to design?"
27
+ class="tw-flex-1 tw-bg-transparent tw-border-none tw-text-white tw-placeholder-grayscale-500 focus:tw-outline-none tw-text-base"
31
28
  />
32
29
  <VoiceToTextButton
30
+ variant="dark"
31
+ size="md"
33
32
  @on-transcript="transcript = $event"
34
33
  @on-interim-transcript="transcript = $event"
35
34
  @on-start="isListening = true; error = null"
36
35
  @on-stop="isListening = false"
37
36
  @on-error="error = $event"
38
37
  />
38
+ <button class="tw-w-10 tw-h-10 tw-rounded-full tw-bg-secondary-500 tw-flex tw-items-center tw-justify-center tw-text-white hover:tw-bg-secondary-600 tw-transition-colors">
39
+ <svg xmlns="http://www.w3.org/2000/svg" class="tw-w-5 tw-h-5" viewBox="0 0 20 20" fill="currentColor">
40
+ <path fill-rule="evenodd" d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z" clip-rule="evenodd" />
41
+ </svg>
42
+ </button>
39
43
  </div>
40
- <div class="tw-text-sm tw-text-grayscale-600">
41
- <p v-if="isListening" class="tw-text-primary-500 tw-font-medium">
42
- Listening... speak now
44
+
45
+ <div class="tw-mt-4 tw-text-sm tw-h-6">
46
+ <p v-if="isListening" class="tw-text-error-500 tw-font-medium">
47
+ Recording... speak now
43
48
  </p>
44
- <p v-else-if="error" class="tw-text-error-500">
49
+ <p v-else-if="error" class="tw-text-error-400">
45
50
  {{ error }}
46
51
  </p>
47
- <p v-else>
48
- Click the microphone button to start voice input
49
- </p>
50
52
  </div>
51
53
  </div>
52
54
  `,
53
55
  });
54
56
 
55
- WithTranscriptDisplay.story = {
56
- name: 'Interactive Demo',
57
+ PromptInputDemo.story = {
58
+ name: 'Dark - Prompt Input',
57
59
  };
58
60
 
59
- export const Disabled = () => ({
61
+ export const DarkVariantStates = () => ({
60
62
  components: { VoiceToTextButton },
61
63
  template: `
62
- <div class="tw-flex tw-gap-4 tw-items-center">
63
- <div class="tw-text-center">
64
- <VoiceToTextButton />
65
- <p class="tw-text-xs tw-mt-2 tw-text-grayscale-600">Enabled</p>
66
- </div>
67
- <div class="tw-text-center">
68
- <VoiceToTextButton :disabled="true" />
69
- <p class="tw-text-xs tw-mt-2 tw-text-grayscale-600">Disabled</p>
64
+ <div class="tw-p-8" style="background: #1a1a2e;">
65
+ <h3 class="tw-text-white tw-text-lg tw-font-semibold tw-mb-6">Dark Variant - All States</h3>
66
+
67
+ <div class="tw-flex tw-gap-8 tw-items-start">
68
+ <div class="tw-text-center">
69
+ <div class="tw-mb-2 tw-p-4 tw-bg-grayscale-800 tw-rounded-lg tw-inline-block">
70
+ <VoiceToTextButton variant="dark" size="sm" />
71
+ </div>
72
+ <p class="tw-text-grayscale-400 tw-text-xs">Small</p>
73
+ </div>
74
+
75
+ <div class="tw-text-center">
76
+ <div class="tw-mb-2 tw-p-4 tw-bg-grayscale-800 tw-rounded-lg tw-inline-block">
77
+ <VoiceToTextButton variant="dark" size="md" />
78
+ </div>
79
+ <p class="tw-text-grayscale-400 tw-text-xs">Medium (default)</p>
80
+ </div>
81
+
82
+ <div class="tw-text-center">
83
+ <div class="tw-mb-2 tw-p-4 tw-bg-grayscale-800 tw-rounded-lg tw-inline-block">
84
+ <VoiceToTextButton variant="dark" size="lg" />
85
+ </div>
86
+ <p class="tw-text-grayscale-400 tw-text-xs">Large</p>
87
+ </div>
88
+
89
+ <div class="tw-text-center">
90
+ <div class="tw-mb-2 tw-p-4 tw-bg-grayscale-800 tw-rounded-lg tw-inline-block">
91
+ <VoiceToTextButton variant="dark" :disabled="true" />
92
+ </div>
93
+ <p class="tw-text-grayscale-400 tw-text-xs">Disabled</p>
94
+ </div>
70
95
  </div>
96
+
97
+ <p class="tw-text-grayscale-500 tw-text-sm tw-mt-6">
98
+ Click any button to see the recording state with grey circle indicator
99
+ </p>
71
100
  </div>
72
101
  `,
73
102
  });
74
103
 
75
- Disabled.story = {
76
- name: 'Disabled State',
104
+ DarkVariantStates.story = {
105
+ name: 'Dark - Variant States',
77
106
  };
78
107
 
79
- export const LanguageVariants = () => ({
108
+ export const LightInputDemo = () => ({
80
109
  components: { VoiceToTextButton },
81
110
  data() {
82
111
  return {
83
- languages: [
84
- { code: 'en-US', name: 'English (US)' },
85
- { code: 'de-DE', name: 'German' },
86
- { code: 'fr-FR', name: 'French' },
87
- { code: 'es-ES', name: 'Spanish' },
88
- ],
112
+ transcript: '',
113
+ isListening: false,
114
+ error: null,
89
115
  };
90
116
  },
91
117
  template: `
92
- <div class="tw-flex tw-flex-wrap tw-gap-4">
93
- <div
94
- v-for="lang in languages"
95
- :key="lang.code"
96
- class="tw-text-center tw-p-2 tw-border tw-border-grayscale-300 tw-rounded"
97
- >
98
- <VoiceToTextButton :lang="lang.code" />
99
- <p class="tw-text-xs tw-mt-2">{{ lang.name }}</p>
118
+ <div class="tw-min-h-[400px] tw-p-8 tw-flex tw-flex-col tw-items-center tw-justify-center tw-bg-white">
119
+ <h1 class="tw-text-grayscale-800 tw-text-3xl tw-font-bold tw-mb-2 tw-text-center">Search</h1>
120
+ <p class="tw-text-grayscale-600 tw-mb-8 tw-text-center">Find what you're looking for</p>
121
+
122
+ <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">
123
+ <input
124
+ v-model="transcript"
125
+ type="text"
126
+ placeholder="Type to search..."
127
+ 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"
128
+ />
129
+ <VoiceToTextButton
130
+ variant="light"
131
+ size="md"
132
+ @on-transcript="transcript = $event"
133
+ @on-interim-transcript="transcript = $event"
134
+ @on-start="isListening = true; error = null"
135
+ @on-stop="isListening = false"
136
+ @on-error="error = $event"
137
+ />
138
+ </div>
139
+
140
+ <div class="tw-mt-4 tw-text-sm tw-h-6">
141
+ <p v-if="isListening" class="tw-text-error-500 tw-font-medium">
142
+ Recording... speak now
143
+ </p>
144
+ <p v-else-if="error" class="tw-text-error-400">
145
+ {{ error }}
146
+ </p>
147
+ </div>
148
+ </div>
149
+ `,
150
+ });
151
+
152
+ LightInputDemo.story = {
153
+ name: 'Light - Search Input',
154
+ };
155
+
156
+ export const LightVariantStates = () => ({
157
+ components: { VoiceToTextButton },
158
+ template: `
159
+ <div class="tw-p-8 tw-bg-white">
160
+ <h3 class="tw-text-grayscale-800 tw-text-lg tw-font-semibold tw-mb-6">Light Variant - All States</h3>
161
+
162
+ <div class="tw-flex tw-gap-8 tw-items-start">
163
+ <div class="tw-text-center">
164
+ <div class="tw-mb-2 tw-p-4 tw-bg-grayscale-100 tw-rounded-lg tw-inline-block">
165
+ <VoiceToTextButton variant="light" size="sm" />
166
+ </div>
167
+ <p class="tw-text-grayscale-600 tw-text-xs">Small</p>
168
+ </div>
169
+
170
+ <div class="tw-text-center">
171
+ <div class="tw-mb-2 tw-p-4 tw-bg-grayscale-100 tw-rounded-lg tw-inline-block">
172
+ <VoiceToTextButton variant="light" size="md" />
173
+ </div>
174
+ <p class="tw-text-grayscale-600 tw-text-xs">Medium (default)</p>
175
+ </div>
176
+
177
+ <div class="tw-text-center">
178
+ <div class="tw-mb-2 tw-p-4 tw-bg-grayscale-100 tw-rounded-lg tw-inline-block">
179
+ <VoiceToTextButton variant="light" size="lg" />
180
+ </div>
181
+ <p class="tw-text-grayscale-600 tw-text-xs">Large</p>
182
+ </div>
183
+
184
+ <div class="tw-text-center">
185
+ <div class="tw-mb-2 tw-p-4 tw-bg-grayscale-100 tw-rounded-lg tw-inline-block">
186
+ <VoiceToTextButton variant="light" :disabled="true" />
187
+ </div>
188
+ <p class="tw-text-grayscale-600 tw-text-xs">Disabled</p>
189
+ </div>
190
+ </div>
191
+
192
+ <p class="tw-text-grayscale-500 tw-text-sm tw-mt-6">
193
+ Click any button to see the recording state with grey circle indicator
194
+ </p>
195
+ </div>
196
+ `,
197
+ });
198
+
199
+ LightVariantStates.story = {
200
+ name: 'Light - Variant States',
201
+ };
202
+
203
+ export const SideBySide = () => ({
204
+ components: { VoiceToTextButton },
205
+ template: `
206
+ <div class="tw-flex tw-min-h-[300px]">
207
+ <div class="tw-flex-1 tw-p-8 tw-flex tw-flex-col tw-items-center tw-justify-center" style="background: #1a1a2e;">
208
+ <h3 class="tw-text-white tw-text-lg tw-font-semibold tw-mb-6">Dark Variant</h3>
209
+ <div class="tw-flex tw-gap-4 tw-items-center">
210
+ <div class="tw-p-3 tw-bg-grayscale-800 tw-rounded-lg">
211
+ <VoiceToTextButton variant="dark" size="sm" />
212
+ </div>
213
+ <div class="tw-p-3 tw-bg-grayscale-800 tw-rounded-lg">
214
+ <VoiceToTextButton variant="dark" size="md" />
215
+ </div>
216
+ <div class="tw-p-3 tw-bg-grayscale-800 tw-rounded-lg">
217
+ <VoiceToTextButton variant="dark" size="lg" />
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ <div class="tw-flex-1 tw-p-8 tw-flex tw-flex-col tw-items-center tw-justify-center tw-bg-white">
223
+ <h3 class="tw-text-grayscale-800 tw-text-lg tw-font-semibold tw-mb-6">Light Variant</h3>
224
+ <div class="tw-flex tw-gap-4 tw-items-center">
225
+ <div class="tw-p-3 tw-bg-grayscale-100 tw-rounded-lg">
226
+ <VoiceToTextButton variant="light" size="sm" />
227
+ </div>
228
+ <div class="tw-p-3 tw-bg-grayscale-100 tw-rounded-lg">
229
+ <VoiceToTextButton variant="light" size="md" />
230
+ </div>
231
+ <div class="tw-p-3 tw-bg-grayscale-100 tw-rounded-lg">
232
+ <VoiceToTextButton variant="light" size="lg" />
233
+ </div>
234
+ </div>
100
235
  </div>
101
236
  </div>
102
237
  `,
103
238
  });
104
239
 
105
- LanguageVariants.story = {
106
- name: 'Language Variants',
240
+ SideBySide.story = {
241
+ name: 'Side by Side Comparison',
107
242
  };
@@ -1,19 +1,31 @@
1
1
  <template>
2
- <Button
2
+ <button
3
3
  v-if="isSupported"
4
- icon="microphone"
4
+ type="button"
5
5
  :disabled="disabled"
6
- variant="outline"
7
- :class="{ 'voice-listening': isListening }"
8
6
  :aria-label="isListening ? 'Stop voice input' : 'Start voice input'"
9
7
  data-test="voice-to-text-button"
10
- @on-click="toggle"
11
- />
8
+ class="voice-prompt-button tw-rounded-full tw-border-0 tw-flex tw-items-center tw-justify-center tw-transition-all tw-duration-200 tw-cursor-pointer focus:tw-outline-none focus-visible:tw-ring-2 focus-visible:tw-ring-offset-2 focus-visible:tw-ring-primary-500"
9
+ :class="[
10
+ sizeClasses,
11
+ variantClasses.button,
12
+ isListening ? 'voice-prompt-listening' : '',
13
+ disabled ? 'tw-opacity-50 tw-cursor-not-allowed' : '',
14
+ ]"
15
+ :style="isListening ? { '--voice-bg': variantClasses.recordingBg } : {}"
16
+ @click="toggle"
17
+ >
18
+ <Icon
19
+ name="microphone"
20
+ :size="iconSize"
21
+ :class="variantClasses.icon"
22
+ />
23
+ </button>
12
24
  </template>
13
25
 
14
26
  <script setup>
15
- import { watch, toRef } from 'vue';
16
- import Button from '../Button/Button.vue';
27
+ import { watch, toRef, computed } from 'vue';
28
+ import Icon from '../Icon/Icon.vue';
17
29
  import { useVoiceToText } from '../../../useVoiceToText';
18
30
 
19
31
  const props = defineProps({
@@ -25,6 +37,34 @@ const props = defineProps({
25
37
  type: Boolean,
26
38
  default: false,
27
39
  },
40
+ size: {
41
+ type: String,
42
+ default: 'md',
43
+ validator: (value) => ['sm', 'md', 'lg'].includes(value),
44
+ },
45
+ variant: {
46
+ type: String,
47
+ default: 'dark',
48
+ validator: (value) => ['light', 'dark'].includes(value),
49
+ },
50
+ });
51
+
52
+ const sizeClasses = computed(() => {
53
+ const sizes = {
54
+ sm: 'tw-w-8 tw-h-8',
55
+ md: 'tw-w-10 tw-h-10',
56
+ lg: 'tw-w-12 tw-h-12',
57
+ };
58
+ return sizes[props.size];
59
+ });
60
+
61
+ const iconSize = computed(() => {
62
+ const sizes = {
63
+ sm: 'sm',
64
+ md: 'md',
65
+ lg: 'md',
66
+ };
67
+ return sizes[props.size];
28
68
  });
29
69
 
30
70
  const emit = defineEmits(['on-transcript', 'on-interim-transcript', 'on-start', 'on-stop', 'on-error']);
@@ -33,6 +73,21 @@ const { isSupported, isListening, transcript, isFinal, error, toggle, setLang }
33
73
  lang: props.lang,
34
74
  });
35
75
 
76
+ const variantClasses = computed(() => {
77
+ if (props.variant === 'light') {
78
+ return {
79
+ button: isListening.value ? '' : 'tw-bg-transparent hover:tw-bg-grayscale-200',
80
+ icon: isListening.value ? 'tw-text-grayscale-700' : 'tw-text-grayscale-600',
81
+ recordingBg: '#EDEDED',
82
+ };
83
+ }
84
+ return {
85
+ button: isListening.value ? '' : 'tw-bg-transparent hover:tw-bg-grayscale-700',
86
+ icon: isListening.value ? 'tw-text-white' : 'tw-text-grayscale-400',
87
+ recordingBg: '#606060',
88
+ };
89
+ });
90
+
36
91
  // Keep recognition language in sync with prop
37
92
  watch(toRef(props, 'lang'), setLang);
38
93
 
@@ -68,17 +123,22 @@ watch(error, (newError) => {
68
123
  </script>
69
124
 
70
125
  <style scoped>
71
- .voice-listening {
72
- animation: voice-pulse 1.5s ease-in-out infinite;
126
+ .voice-prompt-button {
127
+ -webkit-tap-highlight-color: transparent;
128
+ }
129
+
130
+ .voice-prompt-listening {
131
+ background-color: var(--voice-bg);
132
+ animation: voice-bg-pulse 2s ease-in-out infinite;
73
133
  }
74
134
 
75
- @keyframes voice-pulse {
135
+ @keyframes voice-bg-pulse {
76
136
  0%,
77
137
  100% {
78
- box-shadow: 0 0 0 0 var(--color-primary, rgba(242, 27, 63, 0.4));
138
+ opacity: 1;
79
139
  }
80
140
  50% {
81
- box-shadow: 0 0 0 8px transparent;
141
+ opacity: 0.5;
82
142
  }
83
143
  }
84
144
  </style>
@@ -134,6 +134,7 @@ export function useVoiceToText(options = {}) {
134
134
  const stop = () => {
135
135
  if (!recognition || !isListening.value) return;
136
136
 
137
+ isListening.value = false;
137
138
  try {
138
139
  recognition.stop();
139
140
  } catch (e) {