@envive-ai/react-toolkit-v3 0.3.22 → 0.3.24

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 (366) hide show
  1. package/dist/AnimatedText/AnimatedText.d.cts +3 -3
  2. package/dist/AnimatedText/AnimatedText.d.ts +3 -3
  3. package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.cts +2 -2
  4. package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.ts +2 -2
  5. package/dist/CSSVariablesEditor/hooks/useGetCssVariablesOptions.cjs +1 -1
  6. package/dist/CSSVariablesEditor/hooks/useGetCssVariablesOptions.js +1 -1
  7. package/dist/CSSVariablesEditor/hooks/useGetCurrentMerchantColors.cjs +1 -1
  8. package/dist/CSSVariablesEditor/hooks/useGetCurrentMerchantColors.js +1 -1
  9. package/dist/CSSVariablesEditor/hooks/useGetDefaultCssVariables.cjs +4 -4
  10. package/dist/CSSVariablesEditor/hooks/useGetDefaultCssVariables.js +4 -4
  11. package/dist/CSSVariablesEditor/hooks/useHandleUpdateCssVars.cjs +3 -3
  12. package/dist/CSSVariablesEditor/hooks/useHandleUpdateCssVars.js +3 -3
  13. package/dist/Carousel/Carousel.d.cts +2 -2
  14. package/dist/Carousel/components/Badge.cjs +1 -1
  15. package/dist/Carousel/components/Badge.js +1 -1
  16. package/dist/Carousel/components/Container.cjs +1 -1
  17. package/dist/Carousel/components/Container.js +1 -1
  18. package/dist/ChatFooter/ChatFooter.cjs +7 -3
  19. package/dist/ChatFooter/ChatFooter.d.cts +7 -3
  20. package/dist/ChatFooter/ChatFooter.d.ts +7 -3
  21. package/dist/ChatFooter/ChatFooter.js +7 -3
  22. package/dist/ChatFooter/components/Layout.cjs +1 -1
  23. package/dist/ChatFooter/components/Layout.js +1 -1
  24. package/dist/ChatFooter/components/index.d.cts +10 -6
  25. package/dist/ChatFooter/components/index.d.ts +10 -6
  26. package/dist/ChatFooter/types/types.d.cts +16 -0
  27. package/dist/ChatFooter/types/types.d.ts +16 -0
  28. package/dist/ChatHeader/ChatHeader.cjs +2 -1
  29. package/dist/ChatHeader/ChatHeader.d.cts +2 -2
  30. package/dist/ChatHeader/ChatHeader.d.ts +2 -2
  31. package/dist/ChatHeader/ChatHeader.js +2 -1
  32. package/dist/ChatHeader/components/Handle.cjs +2 -2
  33. package/dist/ChatHeader/components/Handle.js +2 -2
  34. package/dist/ChatHeader/hooks/useGetHandleProperties.cjs +4 -2
  35. package/dist/ChatHeader/hooks/useGetHandleProperties.js +4 -2
  36. package/dist/ChatHeader/hooks/useGetLayoutProperties.cjs +1 -1
  37. package/dist/ChatHeader/hooks/useGetLayoutProperties.js +1 -1
  38. package/dist/ChatHeader/hooks/useGetToggleOptionProperties.cjs +1 -1
  39. package/dist/ChatHeader/hooks/useGetToggleOptionProperties.js +1 -1
  40. package/dist/ChatPreview/ChatPreview.cjs +6 -3
  41. package/dist/ChatPreview/ChatPreview.d.cts +2 -2
  42. package/dist/ChatPreview/ChatPreview.d.ts +2 -2
  43. package/dist/ChatPreview/ChatPreview.js +6 -3
  44. package/dist/ChatPreview/index.d.cts +2 -2
  45. package/dist/ChatPreview/index.d.ts +2 -2
  46. package/dist/ChatPreview/types/types.d.cts +13 -1
  47. package/dist/ChatPreview/types/types.d.ts +13 -1
  48. package/dist/ChatPreviewComparison/ChatPreviewComparison.cjs +6 -3
  49. package/dist/ChatPreviewComparison/ChatPreviewComparison.d.cts +2 -2
  50. package/dist/ChatPreviewComparison/ChatPreviewComparison.d.ts +2 -2
  51. package/dist/ChatPreviewComparison/ChatPreviewComparison.js +6 -3
  52. package/dist/ChatPreviewComparison/components/Layout.cjs +2 -2
  53. package/dist/ChatPreviewComparison/components/Layout.js +2 -2
  54. package/dist/ChatPreviewComparison/index.d.cts +2 -2
  55. package/dist/ChatPreviewComparison/index.d.ts +2 -2
  56. package/dist/ChatPreviewComparison/types/types.d.cts +13 -1
  57. package/dist/ChatPreviewComparison/types/types.d.ts +13 -1
  58. package/dist/ChatPreviewLoading/ChatPreviewLoading.cjs +7 -2
  59. package/dist/ChatPreviewLoading/ChatPreviewLoading.d.cts +4 -3
  60. package/dist/ChatPreviewLoading/ChatPreviewLoading.d.ts +2 -1
  61. package/dist/ChatPreviewLoading/ChatPreviewLoading.js +7 -2
  62. package/dist/ChatPreviewLoading/types/types.d.cts +4 -0
  63. package/dist/ChatPreviewLoading/types/types.d.ts +4 -0
  64. package/dist/Container/Container.d.cts +172 -172
  65. package/dist/Container/Container.d.ts +172 -172
  66. package/dist/DesignTokens/DesignTokensComponent.d.cts +2 -2
  67. package/dist/DesignTokens/DesignTokensComponent.d.ts +2 -2
  68. package/dist/DesignTokens/components/FontFamily.cjs +1 -1
  69. package/dist/DesignTokens/components/FontFamily.js +1 -1
  70. package/dist/DesignTokens/components/FontSize.cjs +1 -1
  71. package/dist/DesignTokens/components/FontSize.js +1 -1
  72. package/dist/DesignTokens/components/FontWeight.cjs +1 -1
  73. package/dist/DesignTokens/components/FontWeight.js +1 -1
  74. package/dist/DesignTokens/components/LetterSpacing.cjs +1 -1
  75. package/dist/DesignTokens/components/LetterSpacing.js +1 -1
  76. package/dist/DesignTokens/components/LineHeight.cjs +1 -1
  77. package/dist/DesignTokens/components/LineHeight.js +1 -1
  78. package/dist/DesignTokens/components/Typography.cjs +1 -1
  79. package/dist/DesignTokens/components/Typography.js +1 -1
  80. package/dist/Disclaimer/components/Container.cjs +2 -2
  81. package/dist/Disclaimer/components/Container.js +2 -2
  82. package/dist/DocumentRetrievalCard/DocumentRetrievalCard.d.ts +2 -2
  83. package/dist/DocumentRetrievalCard/components/Image.cjs +1 -1
  84. package/dist/DocumentRetrievalCard/components/Image.js +1 -1
  85. package/dist/DocumentRetrievalCard/components/Layout.cjs +1 -1
  86. package/dist/DocumentRetrievalCard/components/Layout.js +1 -1
  87. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.cjs +1 -1
  88. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.js +1 -1
  89. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Label.cjs +1 -1
  90. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Label.js +1 -1
  91. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Layout.cjs +1 -1
  92. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Layout.js +1 -1
  93. package/dist/FloatingButton/FloatingButton.d.cts +2 -2
  94. package/dist/FloatingButton/components/Button.cjs +1 -1
  95. package/dist/FloatingButton/components/Button.js +1 -1
  96. package/dist/FloatingButton/components/Container.cjs +1 -1
  97. package/dist/FloatingButton/components/Container.js +1 -1
  98. package/dist/FloatingButton/components/Wrapper.cjs +1 -1
  99. package/dist/FloatingButton/components/Wrapper.js +1 -1
  100. package/dist/FloatingChat/FloatingChat.cjs +32 -4
  101. package/dist/FloatingChat/FloatingChat.d.cts +2 -2
  102. package/dist/FloatingChat/FloatingChat.d.ts +2 -2
  103. package/dist/FloatingChat/FloatingChat.js +33 -5
  104. package/dist/FloatingChat/components/AgentMessage.cjs +2 -2
  105. package/dist/FloatingChat/components/AgentMessage.js +2 -2
  106. package/dist/FloatingChat/components/ChatMessages.cjs +1 -1
  107. package/dist/FloatingChat/components/ChatMessages.js +1 -1
  108. package/dist/FloatingChat/components/Layout.cjs +1 -1
  109. package/dist/FloatingChat/components/Layout.js +1 -1
  110. package/dist/FloatingChat/components/ProductResultsModal.cjs +1 -1
  111. package/dist/FloatingChat/components/ProductResultsModal.js +1 -1
  112. package/dist/FloatingChat/components/ResultsGridView.cjs +1 -1
  113. package/dist/FloatingChat/components/ResultsGridView.js +1 -1
  114. package/dist/FloatingChat/components/SalesAgentBadgeContent.cjs +1 -1
  115. package/dist/FloatingChat/components/SalesAgentBadgeContent.js +1 -1
  116. package/dist/FloatingChat/components/SlideChatContent.cjs +1 -1
  117. package/dist/FloatingChat/components/SlideChatContent.js +1 -1
  118. package/dist/FloatingChat/hooks/useChatSuggestions.cjs +3 -5
  119. package/dist/FloatingChat/hooks/useChatSuggestions.js +4 -6
  120. package/dist/Form/Form.cjs +1 -1
  121. package/dist/Form/Form.js +1 -1
  122. package/dist/Form/components/Layout.cjs +1 -1
  123. package/dist/Form/components/Layout.js +1 -1
  124. package/dist/Form/components/SubmitButtonItem.cjs +1 -1
  125. package/dist/Form/components/SubmitButtonItem.js +1 -1
  126. package/dist/Form/components/TextFieldItem.cjs +1 -1
  127. package/dist/Form/components/TextFieldItem.js +1 -1
  128. package/dist/FullPageSalesAgent/FullPageSalesAgent.cjs +29 -2
  129. package/dist/FullPageSalesAgent/FullPageSalesAgent.d.cts +5 -3
  130. package/dist/FullPageSalesAgent/FullPageSalesAgent.d.ts +5 -3
  131. package/dist/FullPageSalesAgent/FullPageSalesAgent.js +30 -3
  132. package/dist/FullPageSalesAgent/components/Layout.cjs +1 -1
  133. package/dist/FullPageSalesAgent/components/Layout.js +1 -1
  134. package/dist/Image/Image.cjs +1 -1
  135. package/dist/Image/Image.d.cts +2 -2
  136. package/dist/Image/Image.d.ts +2 -2
  137. package/dist/Image/Image.js +1 -1
  138. package/dist/ImageGallery/ImageGallery.d.cts +2 -2
  139. package/dist/ImageGallery/ImageGallery.d.ts +2 -2
  140. package/dist/ImageGallery/components/Layout.cjs +1 -1
  141. package/dist/ImageGallery/components/Layout.js +1 -1
  142. package/dist/ImageGallery/utils/functions.cjs +1 -1
  143. package/dist/ImageGallery/utils/functions.js +1 -1
  144. package/dist/MarkdownProcessor/MarkdownProcessor.d.cts +2 -2
  145. package/dist/Message/components/Layout.cjs +1 -1
  146. package/dist/Message/components/Layout.js +1 -1
  147. package/dist/OrderLookupCard/OrderLookupCard.cjs +1 -1
  148. package/dist/OrderLookupCard/OrderLookupCard.js +1 -1
  149. package/dist/OrderLookupCard/components/Layout.cjs +1 -1
  150. package/dist/OrderLookupCard/components/Layout.js +1 -1
  151. package/dist/OrderLookupCard/components/MoreProductsOverlay.cjs +1 -1
  152. package/dist/OrderLookupCard/components/MoreProductsOverlay.js +1 -1
  153. package/dist/OrderLookupCard/components/ProductImageGridItem.cjs +1 -1
  154. package/dist/OrderLookupCard/components/ProductImageGridItem.js +1 -1
  155. package/dist/OrderLookupCard/components/ProductImageItem.cjs +1 -1
  156. package/dist/OrderLookupCard/components/ProductImageItem.js +1 -1
  157. package/dist/OrderLookupCard/components/ProductImagesGrid.cjs +1 -1
  158. package/dist/OrderLookupCard/components/ProductImagesGrid.js +1 -1
  159. package/dist/OrderLookupCard/components/StatusLabel.cjs +1 -1
  160. package/dist/OrderLookupCard/components/StatusLabel.js +1 -1
  161. package/dist/OrderLookupCard/components/TrackOrderLink.cjs +1 -1
  162. package/dist/OrderLookupCard/components/TrackOrderLink.js +1 -1
  163. package/dist/ProductCard/ProductCard.cjs +6 -2
  164. package/dist/ProductCard/ProductCard.d.cts +6 -3
  165. package/dist/ProductCard/ProductCard.d.ts +6 -3
  166. package/dist/ProductCard/ProductCard.js +6 -2
  167. package/dist/ProductCard/index.d.cts +2 -2
  168. package/dist/ProductCard/index.d.ts +2 -2
  169. package/dist/ProductCard/types/index.d.cts +7 -1
  170. package/dist/ProductCard/types/index.d.ts +7 -1
  171. package/dist/PromptButton/PromptButton.cjs +1 -1
  172. package/dist/PromptButton/PromptButton.d.cts +2 -2
  173. package/dist/PromptButton/PromptButton.js +1 -1
  174. package/dist/PromptButton/components/Layout.cjs +1 -1
  175. package/dist/PromptButton/components/Layout.js +1 -1
  176. package/dist/PromptButton/components/Loading.cjs +1 -1
  177. package/dist/PromptButton/components/Loading.js +1 -1
  178. package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.cjs +5 -2
  179. package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.cts +6 -3
  180. package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.ts +6 -3
  181. package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.js +5 -2
  182. package/dist/PromptButtonCarouselWithImage/components/Layout.cjs +1 -1
  183. package/dist/PromptButtonCarouselWithImage/components/Layout.js +1 -1
  184. package/dist/PromptButtonCarouselWithImage/components/Skeleton.cjs +1 -1
  185. package/dist/PromptButtonCarouselWithImage/components/Skeleton.js +1 -1
  186. package/dist/PromptButtonCarouselWithImage/types/types.d.cts +12 -0
  187. package/dist/PromptButtonCarouselWithImage/types/types.d.ts +12 -0
  188. package/dist/PromptCarousel/PromptCarousel.cjs +1 -1
  189. package/dist/PromptCarousel/PromptCarousel.d.cts +2 -2
  190. package/dist/PromptCarousel/PromptCarousel.d.ts +2 -2
  191. package/dist/PromptCarousel/PromptCarousel.js +1 -1
  192. package/dist/PromptCarousel/hooks/useCarouselButtons.cjs +3 -2
  193. package/dist/PromptCarousel/hooks/useCarouselButtons.js +3 -2
  194. package/dist/ReviewCard/ReviewCard.d.cts +2 -2
  195. package/dist/ReviewCard/ReviewCard.d.ts +2 -2
  196. package/dist/ReviewCard/components/Container.cjs +1 -1
  197. package/dist/ReviewCard/components/Container.js +1 -1
  198. package/dist/ReviewCard/components/Rating.cjs +1 -2
  199. package/dist/ReviewCard/components/Rating.js +1 -2
  200. package/dist/ReviewCard/components/ReadMoreButton.cjs +1 -1
  201. package/dist/ReviewCard/components/ReadMoreButton.js +1 -1
  202. package/dist/ReviewCard/components/index.d.cts +6 -6
  203. package/dist/ReviewCard/components/index.d.ts +6 -6
  204. package/dist/SalesAgentProductCard/SalesAgentProductCard.d.cts +2 -2
  205. package/dist/SalesAgentProductCard/components/Container.cjs +1 -1
  206. package/dist/SalesAgentProductCard/components/Container.js +1 -1
  207. package/dist/SalesAgentProductCard/components/ProductImage.cjs +1 -1
  208. package/dist/SalesAgentProductCard/components/ProductImage.js +1 -1
  209. package/dist/SalesAgentProductCard/components/ProductName.cjs +1 -1
  210. package/dist/SalesAgentProductCard/components/ProductName.js +1 -1
  211. package/dist/SalesAgentProductCard/components/index.d.cts +8 -8
  212. package/dist/SocialProof/SocialProof.cjs +6 -3
  213. package/dist/SocialProof/SocialProof.d.cts +2 -2
  214. package/dist/SocialProof/SocialProof.d.ts +2 -2
  215. package/dist/SocialProof/SocialProof.js +6 -3
  216. package/dist/SocialProof/components/Headline.cjs +1 -1
  217. package/dist/SocialProof/components/Headline.js +1 -1
  218. package/dist/SocialProof/components/LayoutFourHorizontal.cjs +1 -1
  219. package/dist/SocialProof/components/LayoutFourHorizontal.js +1 -1
  220. package/dist/SocialProof/components/Textfield.cjs +5 -2
  221. package/dist/SocialProof/components/Textfield.js +5 -2
  222. package/dist/SocialProof/index.d.cts +2 -2
  223. package/dist/SocialProof/index.d.ts +2 -2
  224. package/dist/SocialProof/types/types.d.cts +13 -1
  225. package/dist/SocialProof/types/types.d.ts +13 -1
  226. package/dist/SparkleAnimation/SparkleAnimation.d.cts +2 -2
  227. package/dist/Stack/Stack.d.cts +2 -2
  228. package/dist/Stack/Stack.d.ts +2 -2
  229. package/dist/TextField/TextField.cjs +35 -3
  230. package/dist/TextField/TextField.d.cts +5 -1
  231. package/dist/TextField/TextField.d.ts +5 -1
  232. package/dist/TextField/TextField.js +35 -3
  233. package/dist/TextField/components/Input.cjs +1 -1
  234. package/dist/TextField/components/Input.js +1 -1
  235. package/dist/TextField/components/Layout.cjs +11 -8
  236. package/dist/TextField/components/Layout.js +11 -8
  237. package/dist/TextField/components/SendIcon.cjs +1 -1
  238. package/dist/TextField/components/SendIcon.js +1 -1
  239. package/dist/TextField/components/VoiceInputButton.cjs +45 -0
  240. package/dist/TextField/components/VoiceInputButton.js +39 -0
  241. package/dist/TextField/components/index.cjs +3 -1
  242. package/dist/TextField/components/index.js +3 -1
  243. package/dist/TextField/hooks/useGetMicButtonContainerProperties.cjs +20 -0
  244. package/dist/TextField/hooks/useGetMicButtonContainerProperties.js +19 -0
  245. package/dist/TextField/hooks/useGetSkeletonProperties.cjs +1 -1
  246. package/dist/TextField/hooks/useGetSkeletonProperties.js +1 -1
  247. package/dist/TextField/hooks/useVoiceInput.cjs +52 -0
  248. package/dist/TextField/hooks/useVoiceInput.js +50 -0
  249. package/dist/TextField/types/index.d.cts +11 -0
  250. package/dist/TextField/types/index.d.ts +11 -0
  251. package/dist/TextField/utils/getLayoutStateProperties.cjs +9 -1
  252. package/dist/TextField/utils/getLayoutStateProperties.js +8 -1
  253. package/dist/Title/Title.cjs +1 -1
  254. package/dist/Title/Title.js +1 -1
  255. package/dist/Title/components/Layout.cjs +1 -1
  256. package/dist/Title/components/Layout.js +1 -1
  257. package/dist/TitledPromptCarousel/TitledPromptCarousel.cjs +1 -1
  258. package/dist/TitledPromptCarousel/TitledPromptCarousel.d.cts +2 -2
  259. package/dist/TitledPromptCarousel/TitledPromptCarousel.js +1 -1
  260. package/dist/Tokens/index.cjs +6 -6
  261. package/dist/Tokens/index.js +6 -6
  262. package/dist/TypingAnimation/TypingAnimation.cjs +7 -3
  263. package/dist/TypingAnimation/TypingAnimation.d.cts +2 -2
  264. package/dist/TypingAnimation/TypingAnimation.d.ts +2 -2
  265. package/dist/TypingAnimation/TypingAnimation.js +7 -3
  266. package/dist/TypingAnimation/index.d.cts +2 -2
  267. package/dist/TypingAnimation/index.d.ts +2 -2
  268. package/dist/TypingAnimation/types/index.d.cts +13 -1
  269. package/dist/TypingAnimation/types/index.d.ts +13 -1
  270. package/dist/Typography/Typography.d.cts +4 -4
  271. package/dist/Typography/Typography.d.ts +4 -4
  272. package/dist/WelcomeMessage/components/Container.cjs +1 -1
  273. package/dist/WelcomeMessage/components/Container.js +1 -1
  274. package/dist/WidgetTextField/WidgetTextField.cjs +39 -7
  275. package/dist/WidgetTextField/WidgetTextField.d.cts +7 -3
  276. package/dist/WidgetTextField/WidgetTextField.d.ts +7 -3
  277. package/dist/WidgetTextField/WidgetTextField.js +33 -2
  278. package/dist/WidgetTextField/components/Container.cjs +32 -26
  279. package/dist/WidgetTextField/components/Container.js +32 -26
  280. package/dist/WidgetTextField/components/Skeleton.cjs +1 -1
  281. package/dist/WidgetTextField/components/Skeleton.js +1 -1
  282. package/dist/WidgetTextField/hooks/useGetContainerProperties.cjs +5 -3
  283. package/dist/WidgetTextField/hooks/useGetContainerProperties.js +5 -3
  284. package/dist/WidgetTextField/hooks/useGetMicWidgetButtonProperties.cjs +20 -0
  285. package/dist/WidgetTextField/hooks/useGetMicWidgetButtonProperties.js +19 -0
  286. package/dist/WidgetTextField/types/types.d.cts +21 -0
  287. package/dist/WidgetTextField/types/types.d.ts +21 -0
  288. package/dist/WidgetWrapper/WidgetWrapper.cjs +1 -1
  289. package/dist/WidgetWrapper/WidgetWrapper.d.cts +2 -2
  290. package/dist/WidgetWrapper/WidgetWrapper.js +1 -1
  291. package/dist/WidgetWrapper/hooks/useGetWrapperProperties.cjs +1 -1
  292. package/dist/WidgetWrapper/hooks/useGetWrapperProperties.js +1 -1
  293. package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.cts +2 -2
  294. package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.ts +2 -2
  295. package/dist/node_modules/jotai/esm/react.cjs +87 -0
  296. package/dist/node_modules/jotai/esm/react.js +88 -2
  297. package/dist/node_modules/jotai/esm/vanilla/internals.cjs +2 -1
  298. package/dist/node_modules/jotai/esm/vanilla/internals.js +1 -1
  299. package/dist/packages/components-v3/tokens/typography/typography.cjs +1 -1
  300. package/dist/packages/components-v3/tokens/typography/typography.js +1 -1
  301. package/dist/styles.css +1 -1
  302. package/dist/utils/resolveTheme.cjs +1 -1
  303. package/dist/utils/resolveTheme.js +1 -1
  304. package/package.json +2 -1
  305. package/src/components/ChatFooter/ChatFooter.tsx +8 -0
  306. package/src/components/ChatFooter/__tests__/ChatFooter.test.tsx +43 -0
  307. package/src/components/ChatFooter/components/TextField.tsx +12 -0
  308. package/src/components/ChatFooter/types/types.ts +17 -0
  309. package/src/components/ChatHeader/ChatHeader.tsx +1 -0
  310. package/src/components/ChatHeader/components/Handle.tsx +7 -2
  311. package/src/components/ChatHeader/hooks/useGetHandleProperties.ts +5 -1
  312. package/src/components/ChatHeader/hooks/useGetToggleOptionProperties.ts +1 -1
  313. package/src/components/ChatHeader/types/index.ts +1 -0
  314. package/src/components/ChatPreview/ChatPreview.tsx +13 -2
  315. package/src/components/ChatPreview/__tests__/ChatPreview.test.tsx +44 -0
  316. package/src/components/ChatPreview/index.ts +1 -1
  317. package/src/components/ChatPreview/types/types.ts +13 -0
  318. package/src/components/ChatPreviewComparison/ChatPreviewComparison.tsx +6 -0
  319. package/src/components/ChatPreviewComparison/__tests__/ChatPreviewComparison.test.tsx +44 -0
  320. package/src/components/ChatPreviewComparison/index.ts +1 -1
  321. package/src/components/ChatPreviewComparison/types/types.ts +13 -0
  322. package/src/components/ChatPreviewLoading/ChatPreviewLoading.tsx +5 -3
  323. package/src/components/ChatPreviewLoading/__tests__/ChatPreviewLoading.test.tsx +40 -0
  324. package/src/components/ChatPreviewLoading/types/types.ts +5 -0
  325. package/src/components/Disclaimer/components/Container.tsx +1 -1
  326. package/src/components/FloatingChat/FloatingChat.tsx +43 -7
  327. package/src/components/FloatingChat/components/SlideChatContent.tsx +1 -1
  328. package/src/components/FloatingChat/hooks/useChatSuggestions.ts +8 -12
  329. package/src/components/FullPageSalesAgent/FullPageSalesAgent.tsx +43 -7
  330. package/src/components/ProductCard/ProductCard.tsx +7 -0
  331. package/src/components/ProductCard/__tests__/ProductCard.test.tsx +33 -0
  332. package/src/components/ProductCard/index.ts +1 -1
  333. package/src/components/ProductCard/types/index.ts +6 -0
  334. package/src/components/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.tsx +6 -0
  335. package/src/components/PromptButtonCarouselWithImage/__tests__/PromptButtonCarouselWithImage.test.tsx +34 -0
  336. package/src/components/PromptButtonCarouselWithImage/types/types.ts +12 -0
  337. package/src/components/PromptCarousel/__tests__/PromptCarousel.test.tsx +19 -0
  338. package/src/components/PromptCarousel/hooks/useCarouselButtons.ts +4 -2
  339. package/src/components/ReviewCard/components/Rating.tsx +0 -1
  340. package/src/components/SocialProof/SocialProof.tsx +6 -0
  341. package/src/components/SocialProof/__tests__/SocialProof.test.tsx +58 -0
  342. package/src/components/SocialProof/components/Textfield.tsx +9 -0
  343. package/src/components/SocialProof/index.ts +1 -1
  344. package/src/components/SocialProof/types/types.ts +13 -0
  345. package/src/components/TextField/TextField.tsx +49 -0
  346. package/src/components/TextField/__tests__/TextField.test.tsx +3 -3
  347. package/src/components/TextField/__tests__/VoiceInputButton.test.tsx +175 -0
  348. package/src/components/TextField/components/Layout.tsx +24 -17
  349. package/src/components/TextField/components/VoiceInputButton.tsx +69 -0
  350. package/src/components/TextField/components/index.ts +2 -0
  351. package/src/components/TextField/hooks/useGetMicButtonContainerProperties.ts +38 -0
  352. package/src/components/TextField/hooks/useGetSkeletonProperties.ts +1 -1
  353. package/src/components/TextField/hooks/useVoiceInput.ts +75 -0
  354. package/src/components/TextField/types/index.ts +11 -0
  355. package/src/components/TextField/utils/getLayoutStateProperties.ts +8 -0
  356. package/src/components/TypingAnimation/TypingAnimation.tsx +7 -0
  357. package/src/components/TypingAnimation/__tests__/TypingAnimation.test.tsx +47 -0
  358. package/src/components/TypingAnimation/index.ts +1 -1
  359. package/src/components/TypingAnimation/types/index.ts +14 -1
  360. package/src/components/WidgetTextField/WidgetTextField.tsx +47 -0
  361. package/src/components/WidgetTextField/__tests__/WidgetTextField.test.tsx +119 -4
  362. package/src/components/WidgetTextField/components/Container.tsx +40 -27
  363. package/src/components/WidgetTextField/hooks/useGetContainerProperties.ts +16 -4
  364. package/src/components/WidgetTextField/hooks/useGetMicWidgetButtonProperties.ts +38 -0
  365. package/src/components/WidgetTextField/types/types.ts +21 -0
  366. package/src/components/WidgetWrapper/hooks/useGetWrapperProperties.ts +1 -1
@@ -395,6 +395,25 @@ describe('PromptCarousel', () => {
395
395
  expect(screen.getByText('Button 4')).toBeInTheDocument();
396
396
  });
397
397
 
398
+ it('should place two buttons per row when four prompts and ALWAYS_TWO', () => {
399
+ const { container } = render(
400
+ <PromptCarousel
401
+ id="test-carousel"
402
+ promptButtonTexts={['Button 1', 'Button 2', 'Button 3', 'Button 4']}
403
+ promptCarouselRows={PromptCarouselRows.ALWAYS_TWO}
404
+ handleButtonClick={vi.fn()}
405
+ />,
406
+ );
407
+
408
+ const rows = container.querySelectorAll('.envive-tw-overflow-x-auto');
409
+ expect(rows).toHaveLength(2);
410
+ expect(rows[0].textContent).toContain('Button 1');
411
+ expect(rows[0].textContent).toContain('Button 2');
412
+ expect(rows[0].textContent).not.toContain('Button 3');
413
+ expect(rows[1].textContent).toContain('Button 3');
414
+ expect(rows[1].textContent).toContain('Button 4');
415
+ });
416
+
398
417
  it('should render two rows on mobile when promptCarouselRows is TWO_ON_MOBILE_ONE_ON_DESKTOP', () => {
399
418
  mockUseCheckIsMobile.mockReturnValue({ isMobile: true, viewportWidth: 400 });
400
419
 
@@ -5,6 +5,8 @@ interface UseCarouselButtonsProps {
5
5
  shouldShowTwoRows: boolean;
6
6
  }
7
7
 
8
+ const getFirstRowCount = (length: number) => Math.ceil(length / 2);
9
+
8
10
  export const useCarouselButtons = ({
9
11
  promptButtonTexts,
10
12
  shouldShowTwoRows,
@@ -14,7 +16,7 @@ export const useCarouselButtons = ({
14
16
  return [];
15
17
  }
16
18
  return shouldShowTwoRows
17
- ? promptButtonTexts.slice(0, Math.ceil((promptButtonTexts.length + 1) / 2))
19
+ ? promptButtonTexts.slice(0, getFirstRowCount(promptButtonTexts.length))
18
20
  : promptButtonTexts;
19
21
  }, [promptButtonTexts, shouldShowTwoRows]);
20
22
 
@@ -23,7 +25,7 @@ export const useCarouselButtons = ({
23
25
  return [];
24
26
  }
25
27
  return promptButtonTexts.slice(
26
- Math.ceil((promptButtonTexts.length + 1) / 2),
28
+ getFirstRowCount(promptButtonTexts.length),
27
29
  promptButtonTexts.length,
28
30
  );
29
31
  }, [promptButtonTexts]);
@@ -36,7 +36,6 @@ export const Rating = ({ rating, theme, style }: RatingProps) => {
36
36
  variant={TypographyVariant.B3_RG}
37
37
  color={TypographyColor.TEXT_PRIMARY}
38
38
  style={style}
39
- noWrap
40
39
  >
41
40
  {formattedRating}
42
41
  </Typography>
@@ -44,6 +44,7 @@ export const SocialProof = ({
44
44
  images,
45
45
  logoSrc,
46
46
  titleLabel,
47
+ voiceInputEnabled,
47
48
  } = widgetContentProps ?? {};
48
49
 
49
50
  const numberOfCustomersText = useSocialProofCount({
@@ -63,6 +64,8 @@ export const SocialProof = ({
63
64
  handleSecondaryButtonTouchStart,
64
65
  handleSecondaryButtonTouchEnd,
65
66
  handleTextFieldClick,
67
+ onTranscriptionStarted,
68
+ onTranscriptionCompleted,
66
69
  } = widgetEventProps ?? {};
67
70
 
68
71
  const finalTheme = resolveTheme(theme);
@@ -120,6 +123,9 @@ export const SocialProof = ({
120
123
  placeholder={textFieldPlaceholderText}
121
124
  handleTextFieldClick={handleTextFieldClick}
122
125
  iconVariant={hasDynamicLayout ? IconVariant.DEFAULT : IconVariant.SEARCH}
126
+ voiceInputEnabled={voiceInputEnabled}
127
+ onTranscriptionStarted={onTranscriptionStarted}
128
+ onTranscriptionCompleted={onTranscriptionCompleted}
123
129
  />
124
130
  );
125
131
 
@@ -531,4 +531,62 @@ describe('SocialProof', () => {
531
531
  expect(screen.getByText(/\d+ customers/)).toBeInTheDocument();
532
532
  });
533
533
  });
534
+
535
+ describe('Voice input', () => {
536
+ it('should render with voiceInputEnabled set to true', async () => {
537
+ render(
538
+ <SocialProof
539
+ {...defaultProps}
540
+ widgetContentProps={{
541
+ ...defaultProps.widgetContentProps,
542
+ voiceInputEnabled: true,
543
+ }}
544
+ widgetStyleProps={{
545
+ hideTextField: false,
546
+ imageGalleryLayout: ImageGalleryLayout.FOUR_GRID,
547
+ }}
548
+ widgetEventProps={{ handleTextFieldClick: vi.fn() }}
549
+ />,
550
+ );
551
+ await waitFor(() => {
552
+ expect(screen.getByText('What can I help you find?')).toBeInTheDocument();
553
+ });
554
+ });
555
+
556
+ it('should render with voiceInputEnabled set to false', () => {
557
+ render(
558
+ <SocialProof
559
+ {...defaultProps}
560
+ widgetContentProps={{
561
+ ...defaultProps.widgetContentProps,
562
+ voiceInputEnabled: false,
563
+ }}
564
+ widgetStyleProps={{
565
+ hideTextField: false,
566
+ imageGalleryLayout: ImageGalleryLayout.FOUR_GRID,
567
+ }}
568
+ widgetEventProps={{ handleTextFieldClick: vi.fn() }}
569
+ />,
570
+ );
571
+ expect(screen.getByText(/\d+ customers/)).toBeInTheDocument();
572
+ });
573
+
574
+ it('should render with voiceInputEnabled undefined (default)', () => {
575
+ render(
576
+ <SocialProof
577
+ {...defaultProps}
578
+ widgetContentProps={{
579
+ ...defaultProps.widgetContentProps,
580
+ voiceInputEnabled: undefined,
581
+ }}
582
+ widgetStyleProps={{
583
+ hideTextField: false,
584
+ imageGalleryLayout: ImageGalleryLayout.FOUR_GRID,
585
+ }}
586
+ widgetEventProps={{ handleTextFieldClick: vi.fn() }}
587
+ />,
588
+ );
589
+ expect(screen.getByText(/\d+ customers/)).toBeInTheDocument();
590
+ });
591
+ });
534
592
  });
@@ -8,6 +8,9 @@ export type TextfieldProps = {
8
8
  placeholder: string;
9
9
  handleTextFieldClick: (text: string) => void;
10
10
  iconVariant?: IconVariant;
11
+ voiceInputEnabled?: boolean;
12
+ onTranscriptionStarted?: () => void;
13
+ onTranscriptionCompleted?: (transcript: string) => void;
11
14
  };
12
15
 
13
16
  export const Textfield = ({
@@ -15,6 +18,9 @@ export const Textfield = ({
15
18
  placeholder,
16
19
  handleTextFieldClick,
17
20
  iconVariant = IconVariant.DEFAULT,
21
+ voiceInputEnabled,
22
+ onTranscriptionStarted,
23
+ onTranscriptionCompleted,
18
24
  }: TextfieldProps) => {
19
25
  const finalTheme = resolveTheme(theme);
20
26
 
@@ -24,6 +30,9 @@ export const Textfield = ({
24
30
  placeholder={placeholder}
25
31
  iconVariant={iconVariant}
26
32
  onClick={() => handleTextFieldClick(placeholder)}
33
+ enableVoiceInput={voiceInputEnabled}
34
+ onTranscriptionStarted={onTranscriptionStarted}
35
+ onTranscriptionCompleted={onTranscriptionCompleted}
27
36
  />
28
37
  );
29
38
  };
@@ -1,4 +1,4 @@
1
1
  export { SocialProof } from './SocialProof';
2
- export type { SocialProofProps } from './types/types';
2
+ export type { SocialProofProps, WidgetEventProps } from './types/types';
3
3
  export { DynamicLayout, PageVariant, WidgetKind } from './types/types';
4
4
  export { useSocialProofCount } from './hooks';
@@ -114,6 +114,11 @@ export type WidgetContentProps = {
114
114
  * - `FOUR_HORIZONTAL` and `FOUR_GRID`: require 4 images
115
115
  */
116
116
  images?: ImageGalleryImage[];
117
+
118
+ /**
119
+ * Whether to enable voice input.
120
+ */
121
+ voiceInputEnabled?: boolean;
117
122
  };
118
123
 
119
124
  /**
@@ -281,6 +286,14 @@ export type WidgetEventProps = {
281
286
  * @param text - The placeholder text of the text field
282
287
  */
283
288
  handleTextFieldClick?: (text: string) => void;
289
+ /**
290
+ * Callback function invoked when voice transcription starts.
291
+ */
292
+ onTranscriptionStarted?: () => void;
293
+ /**
294
+ * Callback function invoked when voice transcription completes.
295
+ */
296
+ onTranscriptionCompleted?: (transcript: string) => void;
284
297
  };
285
298
 
286
299
  /**
@@ -1,11 +1,14 @@
1
1
  import classNames from 'classnames';
2
+ import { useCallback } from 'react';
2
3
  import { Theme } from '../../../tokens/theme/theme';
3
4
  import { resolveTheme } from '../utils/resolveTheme';
4
5
  import { TextFieldComponents } from './components';
6
+ import { useGetMicButtonContainerProperties } from './hooks/useGetMicButtonContainerProperties';
5
7
  import { useGetSkeletonProperties } from './hooks/useGetSkeletonProperties';
6
8
  import { useTextFieldFocus } from './hooks/useTextFieldFocus';
7
9
  import { useTextFieldSubmit } from './hooks/useTextFieldSubmit';
8
10
  import { useTextFieldValue } from './hooks/useTextFieldValue';
11
+ import { useVoiceInput } from './hooks/useVoiceInput';
9
12
  import type { TextFieldProps } from './types';
10
13
 
11
14
  /**
@@ -23,9 +26,13 @@ export const TextField = ({
23
26
  id,
24
27
  testId,
25
28
  className,
29
+ inputClassName,
26
30
  style,
27
31
  ariaLabel,
28
32
  isLoading = false,
33
+ enableVoiceInput = false,
34
+ onTranscriptionStarted,
35
+ onTranscriptionCompleted,
29
36
  }: TextFieldProps): JSX.Element => {
30
37
  const { currentValue, hasValue, handleChange, resetValue } = useTextFieldValue(
31
38
  controlledValue,
@@ -41,9 +48,30 @@ export const TextField = ({
41
48
  resetValue,
42
49
  );
43
50
 
51
+ const handleVoiceTranscript = useCallback(
52
+ (transcript: string) => {
53
+ resetValue();
54
+ handleChange(transcript);
55
+ onTranscriptionCompleted?.(transcript);
56
+ },
57
+ [handleChange, resetValue, onTranscriptionCompleted],
58
+ );
59
+
60
+ const {
61
+ isListening,
62
+ handleToggleListening,
63
+ handleAbortListening,
64
+ browserSupportsSpeechRecognition,
65
+ } = useVoiceInput({
66
+ onTranscriptionStarted,
67
+ onTranscriptionCompleted: handleVoiceTranscript,
68
+ disabled,
69
+ });
70
+
44
71
  const handleInternalFocus = () => {
45
72
  handleFocus();
46
73
  onFocus?.();
74
+ handleAbortListening();
47
75
  };
48
76
  const { skeletonClass } = useGetSkeletonProperties(isLoading);
49
77
 
@@ -60,6 +88,7 @@ export const TextField = ({
60
88
  onBlur={handleBlur}
61
89
  disabled={disabled || isLoading}
62
90
  ariaLabel={ariaLabel}
91
+ className={inputClassName}
63
92
  />
64
93
  );
65
94
 
@@ -73,11 +102,31 @@ export const TextField = ({
73
102
  />
74
103
  ) : null;
75
104
 
105
+ const { micButtonContainerClasses } = useGetMicButtonContainerProperties({
106
+ theme: resolvedTheme,
107
+ isListening,
108
+ });
109
+
110
+ const voiceInputButtonClassName = classNames(micButtonContainerClasses, skeletonClass);
111
+
112
+ const voiceInputButton =
113
+ enableVoiceInput && browserSupportsSpeechRecognition ? (
114
+ <TextFieldComponents.VoiceInputButton
115
+ theme={resolvedTheme}
116
+ isListening={isListening}
117
+ onToggleListening={handleToggleListening}
118
+ ariaLabel={ariaLabel}
119
+ isLoading={isLoading}
120
+ className={voiceInputButtonClassName}
121
+ />
122
+ ) : null;
123
+
76
124
  return (
77
125
  <TextFieldComponents.Layout
78
126
  theme={resolvedTheme}
79
127
  input={input}
80
128
  sendIcon={sendIcon}
129
+ voiceInputButton={voiceInputButton}
81
130
  isFocused={isFocused}
82
131
  hasValue={hasValue}
83
132
  disabled={disabled}
@@ -69,7 +69,7 @@ describe('TextField', () => {
69
69
  placeholder="Ask me anything..."
70
70
  />,
71
71
  );
72
- const element = container.firstChild as HTMLElement;
72
+ const element = container.querySelector('.envive-tw-flex-1') as HTMLElement;
73
73
  expect(element).toHaveStyle({ marginTop: '20px' });
74
74
  });
75
75
  });
@@ -410,7 +410,7 @@ describe('TextField', () => {
410
410
  placeholder="Ask me anything..."
411
411
  />,
412
412
  );
413
- const element = container.firstChild as HTMLElement;
413
+ const element = container.querySelector('.envive-tw-flex-1') as HTMLElement;
414
414
  expect(element.className).toContain('envive-tw-animate-pulse');
415
415
  });
416
416
 
@@ -422,7 +422,7 @@ describe('TextField', () => {
422
422
  placeholder="Ask me anything..."
423
423
  />,
424
424
  );
425
- const element = container.firstChild as HTMLElement;
425
+ const element = container.querySelector('.envive-tw-flex-1') as HTMLElement;
426
426
  expect(element.className).toContain('envive-tw-h-[40px]');
427
427
  });
428
428
 
@@ -0,0 +1,175 @@
1
+ /**
2
+ * VoiceInputButton Tests
3
+ *
4
+ * These tests validate the voice input functionality used by the VoiceInputButton component.
5
+ * The useVoiceInput hook handles speech recognition, including starting/stopping listening,
6
+ * managing mic permissions, and clearing previous transcriptions.
7
+ */
8
+ import { act, renderHook } from '@testing-library/react';
9
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
10
+ import { useVoiceInput } from '../hooks/useVoiceInput';
11
+
12
+ /**
13
+ * Mock setup using vi.hoisted() to create spies that can be referenced before the mock is called.
14
+ * This is necessary because vi.mock() is hoisted to the top of the file and needs these
15
+ * function references to be available at module evaluation time.
16
+ */
17
+ const spies = vi.hoisted(() => ({
18
+ // Mock functions for SpeechRecognition API methods
19
+ mockStartListening: vi.fn(() => Promise.resolve()),
20
+ mockStopListening: vi.fn(() => Promise.resolve()),
21
+ mockAbortListening: vi.fn(() => Promise.resolve()),
22
+ // Mock for the resetTranscript function from useSpeechRecognition
23
+ mockResetTranscript: vi.fn(),
24
+ }));
25
+
26
+ /**
27
+ * Mock the react-speech-recognition module to control its behavior in tests.
28
+ * This allows us to:
29
+ * - Control whether the browser supports speech recognition
30
+ * - Control what transcript value is returned
31
+ * - Verify that SpeechRecognition methods are called correctly
32
+ */
33
+ vi.mock('react-speech-recognition', () => ({
34
+ // The default export contains the static methods like startListening, stopListening, abortListening
35
+ default: {
36
+ startListening: spies.mockStartListening,
37
+ stopListening: spies.mockStopListening,
38
+ abortListening: spies.mockAbortListening,
39
+ },
40
+ // useSpeechRecognition hook provides transcript state and resetTranscript function
41
+ useSpeechRecognition: vi.fn(() => ({
42
+ transcript: '',
43
+ browserSupportsSpeechRecognition: true,
44
+ resetTranscript: spies.mockResetTranscript,
45
+ })),
46
+ }));
47
+
48
+ describe('useVoiceInput', () => {
49
+ // Reset mocks before each test to ensure clean state
50
+ beforeEach(() => {
51
+ vi.clearAllMocks();
52
+ // Reset the implementation to default successful behavior
53
+ spies.mockStartListening.mockImplementation(() => Promise.resolve());
54
+ });
55
+
56
+ describe('Basic initialization', () => {
57
+ it('should initialize with isListening as false', () => {
58
+ const { result } = renderHook(() => useVoiceInput({}));
59
+ // Voice input should not be active when the hook first mounts
60
+ expect(result.current.isListening).toBe(false);
61
+ });
62
+
63
+ it('should report browser supports speech recognition', () => {
64
+ const { result } = renderHook(() => useVoiceInput({}));
65
+ // Check if the browser has speech recognition support
66
+ expect(result.current.browserSupportsSpeechRecognition).toBe(true);
67
+ });
68
+ });
69
+
70
+ describe('Start listening', () => {
71
+ it('should start listening when button is clicked', async () => {
72
+ const { result } = renderHook(() => useVoiceInput({}));
73
+
74
+ await act(async () => {
75
+ // Call handleToggleListening to start listening
76
+ result.current.handleToggleListening();
77
+ });
78
+
79
+ // Verify that listening state changed to active
80
+ expect(result.current.isListening).toBe(true);
81
+ });
82
+
83
+ it('should delete previous transcription when starting to listen', async () => {
84
+ const { result } = renderHook(() => useVoiceInput({}));
85
+
86
+ await act(async () => {
87
+ result.current.handleToggleListening();
88
+ });
89
+
90
+ // Verify resetTranscript was called BEFORE starting new listening session
91
+ // This ensures previous transcription is cleared when starting fresh
92
+ expect(spies.mockResetTranscript).toHaveBeenCalled();
93
+ });
94
+ });
95
+
96
+ describe('Stop listening', () => {
97
+ it('should stop listening and call onTranscript with current transcript', async () => {
98
+ const onTranscriptionCompleted = vi.fn();
99
+
100
+ const { result } = renderHook(() =>
101
+ useVoiceInput({
102
+ onTranscriptionCompleted,
103
+ }),
104
+ );
105
+
106
+ // First toggle: start listening
107
+ await act(async () => {
108
+ result.current.handleToggleListening();
109
+ });
110
+
111
+ // Second toggle: stop listening - this should call onTranscript callback
112
+ await act(async () => {
113
+ result.current.handleToggleListening();
114
+ });
115
+
116
+ // Verify that stopListening was called to stop speech recognition
117
+ expect(spies.mockStopListening).toHaveBeenCalled();
118
+ // Verify that the onTranscript callback was called with current transcript
119
+ expect(onTranscriptionCompleted).toHaveBeenCalledWith('');
120
+ });
121
+ });
122
+
123
+ describe('Mic permission handling', () => {
124
+ it('should handle mic permission denied error', async () => {
125
+ // Configure the mock to throw an error simulating permission denial
126
+ spies.mockStartListening.mockImplementationOnce(() => {
127
+ throw new Error('Permission denied');
128
+ });
129
+
130
+ const { result } = renderHook(() => useVoiceInput({}));
131
+
132
+ await act(async () => {
133
+ result.current.handleToggleListening();
134
+ });
135
+
136
+ // The hook should catch the error and set isListening back to false
137
+ expect(result.current.isListening).toBe(false);
138
+ });
139
+
140
+ it('should not start listening when disabled', () => {
141
+ // When disabled prop is true, the hook should not attempt to start listening
142
+ const { result } = renderHook(() => useVoiceInput({ disabled: true }));
143
+
144
+ result.current.handleToggleListening();
145
+
146
+ // isListening should remain false
147
+ expect(result.current.isListening).toBe(false);
148
+ // Verify that startListening was never called
149
+ expect(spies.mockStartListening).not.toHaveBeenCalled();
150
+ });
151
+ });
152
+
153
+ describe('Abort listening', () => {
154
+ it('should abort listening and reset state', async () => {
155
+ const { result } = renderHook(() => useVoiceInput({}));
156
+
157
+ // Start listening first
158
+ await act(async () => {
159
+ result.current.handleToggleListening();
160
+ });
161
+
162
+ expect(result.current.isListening).toBe(true);
163
+
164
+ // Call abort to stop listening immediately without collecting transcript
165
+ await act(async () => {
166
+ result.current.handleAbortListening();
167
+ });
168
+
169
+ // Verify abortListening was called to immediately stop speech recognition
170
+ expect(spies.mockAbortListening).toHaveBeenCalled();
171
+ // Verify state is reset to false
172
+ expect(result.current.isListening).toBe(false);
173
+ });
174
+ });
175
+ });
@@ -7,6 +7,7 @@ type LayoutProps = {
7
7
  theme: Theme;
8
8
  input: React.ReactNode;
9
9
  sendIcon: React.ReactNode;
10
+ voiceInputButton?: React.ReactNode;
10
11
  isFocused: boolean;
11
12
  hasValue: boolean;
12
13
  disabled?: boolean;
@@ -20,6 +21,7 @@ export const Layout = ({
20
21
  theme,
21
22
  input,
22
23
  sendIcon,
24
+ voiceInputButton,
23
25
  isFocused,
24
26
  hasValue,
25
27
  disabled = false,
@@ -37,29 +39,34 @@ export const Layout = ({
37
39
  containerHoverClasses,
38
40
  containerDisabledClasses,
39
41
  } = useGetContainerProperties({ theme });
40
- const { isActive, shouldShowFocus, shouldShowHover } = getLayoutStateProperties(
42
+ const { isActive, shouldShowFocus, shouldShowHover, containerClasses } = getLayoutStateProperties(
43
+ theme,
41
44
  hasValue,
42
45
  isFocused,
43
46
  disabled,
44
47
  );
45
48
 
46
49
  return (
47
- <div
48
- id={id}
49
- data-testid={testId}
50
- className={classNames(
51
- containerLayoutClasses,
52
- containerDefaultClasses,
53
- isActive ? containerBorderActiveClasses : containerBorderClasses,
54
- shouldShowFocus && containerFocusClasses,
55
- shouldShowHover && containerHoverClasses,
56
- disabled && containerDisabledClasses,
57
- className,
58
- )}
59
- style={style}
60
- >
61
- {input}
62
- {sendIcon}
50
+ <div className={containerClasses}>
51
+ <div
52
+ id={id}
53
+ data-testid={testId}
54
+ className={classNames(
55
+ 'envive-tw-flex-1',
56
+ containerLayoutClasses,
57
+ containerDefaultClasses,
58
+ isActive ? containerBorderActiveClasses : containerBorderClasses,
59
+ shouldShowFocus && containerFocusClasses,
60
+ shouldShowHover && containerHoverClasses,
61
+ disabled && containerDisabledClasses,
62
+ className,
63
+ )}
64
+ style={style}
65
+ >
66
+ {input}
67
+ {sendIcon}
68
+ </div>
69
+ {voiceInputButton}
63
70
  </div>
64
71
  );
65
72
  };
@@ -0,0 +1,69 @@
1
+ import Mic from '@envive-ai/react-icons/Mic';
2
+ import MicThin from '@envive-ai/react-icons/MicThin';
3
+ import Stop from '@envive-ai/react-icons/Stop';
4
+ import StopSharp from '@envive-ai/react-icons/StopSharp';
5
+ import StopThin from '@envive-ai/react-icons/StopThin';
6
+ import { Theme } from '../../../../tokens/theme/theme';
7
+
8
+ type VoiceInputButtonProps = {
9
+ theme: Theme;
10
+ isListening?: boolean;
11
+ onToggleListening?: () => void;
12
+ disabled?: boolean;
13
+ className?: string;
14
+ style?: React.CSSProperties;
15
+ ariaLabel?: string;
16
+ isLoading?: boolean;
17
+ };
18
+
19
+ export const VoiceInputButton = ({
20
+ theme,
21
+ isListening,
22
+ onToggleListening,
23
+ disabled = false,
24
+ className,
25
+ style,
26
+ ariaLabel,
27
+ isLoading = false,
28
+ }: VoiceInputButtonProps): JSX.Element => {
29
+ const getMicIcon = (iconTheme: Theme) => {
30
+ switch (iconTheme) {
31
+ case Theme.MODERN:
32
+ return MicThin;
33
+ default:
34
+ return Mic;
35
+ }
36
+ };
37
+
38
+ const getStopIcon = (iconTheme: Theme) => {
39
+ switch (iconTheme) {
40
+ case Theme.MODERN:
41
+ return StopThin;
42
+ case Theme.MINIMAL:
43
+ return StopSharp;
44
+ default:
45
+ return Stop;
46
+ }
47
+ };
48
+
49
+ const MicIcon = getMicIcon(theme);
50
+ const StopIcon = getStopIcon(theme);
51
+
52
+ return (
53
+ <button
54
+ type="button"
55
+ onClick={onToggleListening}
56
+ disabled={disabled || isLoading}
57
+ className={className}
58
+ style={style}
59
+ aria-label={ariaLabel}
60
+ tabIndex={disabled || isLoading ? -1 : 0}
61
+ >
62
+ {isListening ? (
63
+ <StopIcon className="envive-tw-h-[24px]" />
64
+ ) : (
65
+ <MicIcon className="envive-tw-h-[24px]" />
66
+ )}
67
+ </button>
68
+ );
69
+ };
@@ -1,9 +1,11 @@
1
1
  import { Layout } from './Layout';
2
2
  import { Input } from './Input';
3
3
  import { SendIcon } from './SendIcon';
4
+ import { VoiceInputButton } from './VoiceInputButton';
4
5
 
5
6
  export const TextFieldComponents = {
6
7
  Layout,
7
8
  Input,
8
9
  SendIcon,
10
+ VoiceInputButton,
9
11
  };