xertica-ui 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +564 -552
- package/README.md +417 -406
- package/assets/xertica-logo.svg +37 -37
- package/assets/xertica-x-logo.svg +20 -20
- package/bin/cli.ts +1244 -1155
- package/bin/language-config.ts +358 -361
- package/components/assistant/code-block/CodeBlock.tsx +268 -268
- package/components/assistant/formatted-document/FormattedDocument.tsx +147 -147
- package/components/assistant/modern-chat-input/ModernChatInput.tsx +564 -554
- package/components/assistant/xertica-assistant/parts/AssistantCollapsedView.tsx +99 -99
- package/components/assistant/xertica-assistant/parts/AssistantConversationList.tsx +104 -106
- package/components/assistant/xertica-assistant/parts/AssistantDocumentEditor.tsx +81 -81
- package/components/assistant/xertica-assistant/parts/AssistantFeedbackDialog.tsx +88 -78
- package/components/assistant/xertica-assistant/parts/AssistantHeader.tsx +75 -75
- package/components/assistant/xertica-assistant/parts/AssistantMessageBubble.tsx +564 -560
- package/components/assistant/xertica-assistant/parts/AssistantTabBar.tsx +67 -67
- package/components/assistant/xertica-assistant/parts/AssistantWelcomeScreen.tsx +103 -103
- package/components/assistant/xertica-assistant/use-assistant.ts +615 -615
- package/components/assistant/xertica-assistant/xertica-assistant.tsx +611 -613
- package/components/blocks/card-patterns/ActivityCard.tsx +100 -100
- package/components/blocks/card-patterns/ActivityCardSkeleton.tsx +56 -56
- package/components/blocks/card-patterns/FeatureCardSkeleton.tsx +58 -63
- package/components/blocks/card-patterns/NotificationCard.tsx +140 -140
- package/components/blocks/card-patterns/NotificationCardSkeleton.tsx +81 -81
- package/components/blocks/card-patterns/ProfileCard.tsx +112 -114
- package/components/blocks/card-patterns/ProfileCardSkeleton.tsx +69 -69
- package/components/blocks/card-patterns/ProjectCard.tsx +123 -123
- package/components/blocks/card-patterns/ProjectCardSkeleton.tsx +67 -72
- package/components/blocks/card-patterns/QuickActionCardSkeleton.tsx +44 -44
- package/components/blocks/card-patterns/card-patterns.stories.tsx +594 -594
- package/components/blocks/card-patterns/index.ts +29 -29
- package/components/brand/language-selector/LanguageSelector.tsx +102 -102
- package/components/brand/language-selector/language-selector.stories.tsx +111 -114
- package/components/brand/language-selector/language-selector.test.tsx +101 -101
- package/components/brand/theme-toggle/ThemeToggle.tsx +74 -70
- package/components/brand/xertica-provider/XerticaProvider.tsx +109 -112
- package/components/brand/xertica-provider/xertica-provider.mdx +61 -61
- package/components/index.ts +86 -90
- package/components/layout/sidebar/sidebar.mdx +1 -1
- package/components/layout/sidebar/sidebar.tsx +1079 -1073
- package/components/media/FloatingMediaWrapper.tsx +371 -371
- package/components/media/audio-player/AudioPlayer.tsx +768 -766
- package/components/media/video-player/VideoPlayer.tsx +310 -310
- package/components/pages/forgot-password-page/ForgotPasswordPage.tsx +1 -1
- package/components/pages/home-content/HomeContent.tsx +120 -120
- package/components/pages/home-content/home-content.mdx +62 -62
- package/components/pages/home-page/HomePage.tsx +78 -74
- package/components/pages/home-page/home-page.mdx +53 -53
- package/components/pages/login-page/LoginPage.tsx +218 -216
- package/components/pages/reset-password-page/ResetPasswordPage.tsx +243 -239
- package/components/pages/template-content/TemplateContent.tsx +1354 -1235
- package/components/pages/template-content/template-content.mdx +61 -61
- package/components/pages/template-page/template-page.mdx +53 -53
- package/components/pages/verify-email-page/VerifyEmailPage.tsx +206 -206
- package/components/shared/error-boundary.stories.tsx +4 -22
- package/components/shared/error-boundary.tsx +1 -5
- package/components/shared/error-fallbacks.tsx +4 -8
- package/components/ui/accordion/accordion.mdx +8 -8
- package/components/ui/alert/alert.mdx +8 -8
- package/components/ui/alert-dialog/alert-dialog.mdx +8 -8
- package/components/ui/aspect-ratio/aspect-ratio.mdx +8 -8
- package/components/ui/assistant-chart/assistant-chart.mdx +8 -8
- package/components/ui/avatar/avatar.mdx +8 -8
- package/components/ui/badge/badge.mdx +8 -8
- package/components/ui/breadcrumb/breadcrumb.mdx +8 -8
- package/components/ui/button/button.mdx +8 -8
- package/components/ui/calendar/calendar.mdx +8 -8
- package/components/ui/card/card.mdx +8 -8
- package/components/ui/carousel/carousel.mdx +8 -8
- package/components/ui/chart/chart.mdx +8 -8
- package/components/ui/checkbox/checkbox.mdx +8 -8
- package/components/ui/collapsible/collapsible.mdx +8 -8
- package/components/ui/command/command.mdx +8 -8
- package/components/ui/context-menu/context-menu.mdx +8 -8
- package/components/ui/dialog/dialog.mdx +8 -8
- package/components/ui/drawer/drawer.mdx +8 -8
- package/components/ui/dropdown-menu/dropdown-menu.mdx +8 -8
- package/components/ui/empty/empty.mdx +8 -8
- package/components/ui/file-upload/file-upload.mdx +8 -8
- package/components/ui/hover-card/hover-card.mdx +8 -8
- package/components/ui/input/input.mdx +8 -8
- package/components/ui/input-otp/input-otp.mdx +8 -8
- package/components/ui/label/label.mdx +8 -8
- package/components/ui/map/map.mdx +8 -8
- package/components/ui/menubar/menubar.mdx +8 -8
- package/components/ui/navigation-menu/navigation-menu.mdx +8 -8
- package/components/ui/notification-badge/notification-badge.mdx +8 -8
- package/components/ui/pagination/pagination.mdx +8 -8
- package/components/ui/popover/popover.mdx +8 -8
- package/components/ui/progress/progress.mdx +8 -8
- package/components/ui/radio-group/radio-group.mdx +8 -8
- package/components/ui/rating/rating.mdx +8 -8
- package/components/ui/resizable/resizable.mdx +8 -8
- package/components/ui/route-map/route-map.mdx +8 -8
- package/components/ui/scroll-area/scroll-area.mdx +8 -8
- package/components/ui/search/search.mdx +8 -8
- package/components/ui/select/select.mdx +8 -8
- package/components/ui/separator/separator.mdx +8 -8
- package/components/ui/sheet/sheet.mdx +8 -8
- package/components/ui/simple-map/simple-map.mdx +8 -8
- package/components/ui/skeleton/skeleton.mdx +8 -8
- package/components/ui/slider/slider.mdx +8 -8
- package/components/ui/sonner/sonner.mdx +8 -8
- package/components/ui/stats-card/index.ts +2 -2
- package/components/ui/stats-card/stats-card-skeleton.tsx +60 -62
- package/components/ui/stats-card/stats-card.mdx +8 -8
- package/components/ui/stats-card/stats-card.stories.tsx +99 -99
- package/components/ui/stepper/stepper.mdx +8 -8
- package/components/ui/switch/switch.mdx +8 -8
- package/components/ui/table/table.mdx +8 -8
- package/components/ui/tabs/tabs.mdx +8 -8
- package/components/ui/textarea/textarea.mdx +8 -8
- package/components/ui/timeline/timeline.mdx +8 -8
- package/components/ui/toggle/toggle.mdx +8 -8
- package/components/ui/toggle-group/toggle-group.mdx +8 -8
- package/components/ui/tooltip/tooltip.mdx +8 -8
- package/components/ui/tree-view/tree-view.mdx +8 -8
- package/components.json +892 -892
- package/contexts/AuthContext.tsx +11 -8
- package/contexts/LanguageContext.test.tsx +121 -121
- package/contexts/LanguageContext.tsx +250 -251
- package/dist/{AssistantChart-DoZCyS5r.cjs → AssistantChart-9w31gdAb.cjs} +4 -4
- package/dist/{AssistantChart-CldVCVDe.cjs → AssistantChart-BAudAfne.cjs} +5 -5
- package/dist/{AssistantChart-Bdd44uBn.cjs → AssistantChart-BAx9VQvb.cjs} +127 -388
- package/dist/{AssistantChart-Cu3m7RBo.js → AssistantChart-BP8upjMk.js} +5 -5
- package/dist/{AssistantChart-CFhDdGyU.js → AssistantChart-CVko2A1W.js} +130 -391
- package/dist/{AssistantChart-C_hwFRRr.js → AssistantChart-CVzmmhx4.js} +4 -4
- package/dist/{AudioPlayer-IAU5q5T1.cjs → AudioPlayer-1ypwE2Wh.cjs} +1 -1
- package/dist/{AudioPlayer-CGRUtUdN.js → AudioPlayer-DuKXrCfy.js} +1 -1
- package/dist/{LanguageContext-CS14yCpi.js → LanguageContext-BwhwC3G2.js} +2 -2
- package/dist/{LanguageContext-B_KFTCzT.cjs → LanguageContext-DvUt5jBg.cjs} +2 -2
- package/dist/{ThemeContext-C2EwAPDt.js → ThemeContext-BbBNoFTG.js} +2 -2
- package/dist/{ThemeContext-Bmod0Cg2.cjs → ThemeContext-BblcjQup.cjs} +13 -8
- package/dist/{ThemeContext-BWq9ACPo.js → ThemeContext-Bo-W2WZH.js} +13 -8
- package/dist/{ThemeContext-j5aGtPky.cjs → ThemeContext-CP3a0jxy.cjs} +193 -262
- package/dist/{ThemeContext-vTjumZeM.cjs → ThemeContext-Cmr8Ex8H.cjs} +2 -2
- package/dist/ThemeContext-CpqYShLq.cjs +324 -0
- package/dist/{ThemeContext-CQSo4Iwc.js → ThemeContext-D3LzacmG.js} +8 -1
- package/dist/ThemeContext-Du2nE1PL.js +325 -0
- package/dist/ThemeContext-GeEBTJ3q.cjs +1621 -0
- package/dist/ThemeContext-JyLK9B1o.js +1622 -0
- package/dist/{ThemeContext-CGk3KK0k.cjs → ThemeContext-U4dEYc6C.cjs} +8 -1
- package/dist/{ThemeContext-BXjrgUjW.js → ThemeContext-ept8jhXI.js} +200 -261
- package/dist/{VerifyEmailPage-C0c2e5n0.js → VerifyEmailPage-BE-L9mB7.js} +7 -7
- package/dist/{VerifyEmailPage-DSBMRHtl.js → VerifyEmailPage-BIBOKV7Z.js} +41 -36
- package/dist/{VerifyEmailPage-DgIid028.js → VerifyEmailPage-BJjAMUTW.js} +4 -4
- package/dist/{VerifyEmailPage--1Vurewl.cjs → VerifyEmailPage-BRSP-Pwt.cjs} +3 -3
- package/dist/{VerifyEmailPage-Cwi3kbol.cjs → VerifyEmailPage-Bae2cBXT.cjs} +7 -7
- package/dist/{VerifyEmailPage-De6bQjrz.cjs → VerifyEmailPage-BiRm7Nh4.cjs} +41 -36
- package/dist/{VerifyEmailPage-ByerOcm4.cjs → VerifyEmailPage-Bv8Ah_TK.cjs} +23 -20
- package/dist/VerifyEmailPage-Bvfv8HVQ.js +3214 -0
- package/dist/{VerifyEmailPage-BComraR7.cjs → VerifyEmailPage-CR7kb5df.cjs} +22 -12
- package/dist/{VerifyEmailPage-MTD7AG1Z.js → VerifyEmailPage-C_ihbcth.js} +4 -4
- package/dist/{VerifyEmailPage-1WwWczAn.js → VerifyEmailPage-CbgjOF0v.js} +22 -12
- package/dist/{VerifyEmailPage-DvMLZgFt.js → VerifyEmailPage-CdYPSJoO.js} +1 -1
- package/dist/{VerifyEmailPage-By3Jf__L.cjs → VerifyEmailPage-CkBYfsNy.cjs} +4 -4
- package/dist/{VerifyEmailPage-CJLz3jrn.js → VerifyEmailPage-Cyl55sJb.js} +23 -20
- package/dist/VerifyEmailPage-D-FRj5TU.cjs +3213 -0
- package/dist/VerifyEmailPage-DF2ilhum.cjs +3210 -0
- package/dist/{VerifyEmailPage-CYXtbKi3.cjs → VerifyEmailPage-DMBh4NM9.cjs} +1 -1
- package/dist/{VerifyEmailPage-CgMxRb4z.js → VerifyEmailPage-DTtFfC-J.js} +3 -3
- package/dist/{VerifyEmailPage-CFLMls1p.cjs → VerifyEmailPage-Dt7zgA4w.cjs} +4 -4
- package/dist/VerifyEmailPage-EhudUdqF.js +3211 -0
- package/dist/{VerifyEmailPage-C5TNQTBa.js → VerifyEmailPage-X14vhdyl.js} +148 -75
- package/dist/VerifyEmailPage-hdB8JQGv.cjs +3213 -0
- package/dist/{VerifyEmailPage-B4peJjAT.cjs → VerifyEmailPage-u_Dn7t1U.cjs} +148 -75
- package/dist/VerifyEmailPage-vYHbYK3q.js +3214 -0
- package/dist/{XerticaProvider-CBGc4EMA.cjs → XerticaProvider-AChwphCO.cjs} +4 -4
- package/dist/{XerticaProvider-BIrqfZ-i.cjs → XerticaProvider-AbWlr7Af.cjs} +8 -11
- package/dist/{XerticaProvider-D-yNhF94.cjs → XerticaProvider-B8CaV7xu.cjs} +1 -1
- package/dist/{XerticaProvider-DDuiIcKo.js → XerticaProvider-BErr83Bg.js} +14 -11
- package/dist/{XerticaProvider-CEoWMTxu.js → XerticaProvider-BITjgC5p.js} +2 -2
- package/dist/{XerticaProvider-CllrbMEJ.cjs → XerticaProvider-By8q3Roe.cjs} +2 -2
- package/dist/{XerticaProvider-C1DKnvLh.js → XerticaProvider-CUYJZc32.js} +4 -4
- package/dist/{XerticaProvider-ET0ihewn.cjs → XerticaProvider-CW9hpCdF.cjs} +2 -2
- package/dist/{XerticaProvider-Dt5HEzbQ.js → XerticaProvider-CWgby5mY.js} +10 -10
- package/dist/XerticaProvider-CWs6EwNa.js +49 -0
- package/dist/XerticaProvider-CjQAQPcn.cjs +48 -0
- package/dist/XerticaProvider-CwOkHxiT.cjs +44 -0
- package/dist/XerticaProvider-D5lLumH-.js +49 -0
- package/dist/{XerticaProvider-DYq4JWtg.js → XerticaProvider-DQtvJU7m.js} +1 -1
- package/dist/XerticaProvider-qQUDop71.cjs +48 -0
- package/dist/{XerticaProvider-B7EVH-NF.js → XerticaProvider-siSt9uG2.js} +2 -2
- package/dist/{XerticaXLogo-Zw2B276b.cjs → XerticaXLogo-8TTzBjHw.cjs} +1 -1
- package/dist/{XerticaXLogo-B7xQ5dhi.js → XerticaXLogo-BWaag64t.js} +1 -1
- package/dist/{XerticaXLogo-DZbo4vOE.js → XerticaXLogo-BX3ueACh.js} +5 -2
- package/dist/XerticaXLogo-CFuIlYFH.js +252 -0
- package/dist/XerticaXLogo-CU-U-GP4.cjs +251 -0
- package/dist/XerticaXLogo-ChryA6xj.js +252 -0
- package/dist/{XerticaXLogo-CQUUjXoH.cjs → XerticaXLogo-CziKMQil.cjs} +8 -8
- package/dist/XerticaXLogo-DHz5SugF.js +252 -0
- package/dist/XerticaXLogo-DTee_y8X.cjs +251 -0
- package/dist/{XerticaXLogo-Cmsp-Eey.js → XerticaXLogo-DfUvz-lD.js} +9 -9
- package/dist/XerticaXLogo-kslQ8Tk_.cjs +251 -0
- package/dist/{XerticaXLogo-bvZSgwGF.cjs → XerticaXLogo-qBPhwK3g.cjs} +5 -2
- package/dist/{alert-dialog-s-vmNkJ_.js → alert-dialog-iDe5VE5o.js} +3 -3
- package/dist/{alert-dialog-DSKByiKZ.cjs → alert-dialog-yckpaOpy.cjs} +3 -3
- package/dist/assistant.cjs.js +1 -1
- package/dist/assistant.es.js +1 -1
- package/dist/brand.cjs.js +2 -2
- package/dist/brand.es.js +2 -2
- package/dist/cli.js +90 -37
- package/dist/components/brand/theme-toggle/ThemeToggle.d.ts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/{google-maps-loader-Y-QkD-Li.cjs → google-maps-loader-BqsYL48U.cjs} +0 -5
- package/dist/{google-maps-loader-CTYySAun.js → google-maps-loader-t2IlYBzw.js} +0 -4
- package/dist/index-CkTUgOwX.js +8 -0
- package/dist/{index-COtD8bRW.cjs → index-D3RLKRAs.cjs} +1 -1
- package/dist/index.cjs.js +5 -5
- package/dist/index.es.js +5 -5
- package/dist/index.umd.js +454 -1027
- package/dist/layout.cjs.js +1 -1
- package/dist/layout.es.js +1 -1
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/{sidebar-DAaY8bRU.cjs → sidebar-B3EYhli0.cjs} +33 -24
- package/dist/{sidebar-B6SlKZYN.js → sidebar-B4ZWaMrE.js} +1 -1
- package/dist/{sidebar-DQj1z3jG.cjs → sidebar-B9NR0lCe.cjs} +269 -227
- package/dist/{sidebar-nzPoVHBQ.cjs → sidebar-BS1p2V7t.cjs} +1 -1
- package/dist/sidebar-BvF5I2Ue.cjs +800 -0
- package/dist/{sidebar-q7P2Godd.cjs → sidebar-C5B_LHek.cjs} +1 -1
- package/dist/{sidebar-CrQDDdcz.js → sidebar-CA6_ek3f.js} +33 -24
- package/dist/{sidebar-BxGXsDAd.cjs → sidebar-CVUGHOS_.cjs} +8 -16
- package/dist/{sidebar-BViy8Eeu.js → sidebar-CmvwjnVb.js} +9 -17
- package/dist/sidebar-CplprZpM.js +801 -0
- package/dist/{sidebar-BbVIQvlP.js → sidebar-Dz7bd3zP.js} +1 -1
- package/dist/sidebar-KIS0C2JH.js +801 -0
- package/dist/sidebar-OTO_up7Z.js +801 -0
- package/dist/sidebar-zowjejT2.cjs +800 -0
- package/dist/{use-audio-player-nv8ZSGa1.js → use-audio-player-Bkh23vQ3.js} +3 -7
- package/dist/{use-audio-player-NKsWyjWu.cjs → use-audio-player-Dn1NR9xN.cjs} +3 -7
- package/dist/{xertica-assistant-dyP7KHM5.cjs → xertica-assistant-B1IaHXnB.cjs} +388 -529
- package/dist/{xertica-assistant-DCsnQyi5.js → xertica-assistant-B1NaSFFj.js} +46 -29
- package/dist/{xertica-assistant-ciJaWqm1.js → xertica-assistant-BMqdyRVi.js} +10 -28
- package/dist/{xertica-assistant-V_IdW4WF.cjs → xertica-assistant-Bj3vBCq_.cjs} +9 -27
- package/dist/{xertica-assistant-CrgTb6Hs.cjs → xertica-assistant-CIaUlbIt.cjs} +47 -22
- package/dist/{xertica-assistant-yX1CFBBo.js → xertica-assistant-DPsESB6t.js} +390 -531
- package/dist/{CodeBlock-BgfYL_rD.cjs → xertica-assistant-Qp3ydksa.cjs} +51 -263
- package/dist/{CodeBlock-BeSt1h5P.js → xertica-assistant-gnCJdcZY.js} +7 -219
- package/dist/xertica-ui.css +2 -2
- package/docs/architecture-improvements.md +456 -456
- package/docs/architecture.md +312 -306
- package/docs/components/assistant.md +428 -428
- package/docs/components/branding.md +252 -252
- package/docs/components/card-patterns.md +447 -445
- package/docs/components/error-boundary.md +32 -22
- package/docs/components/hooks.md +432 -430
- package/docs/components/language-selector.md +176 -172
- package/docs/components/pages.md +20 -6
- package/docs/doc-audit.md +244 -243
- package/docs/getting-started.md +616 -603
- package/docs/guidelines.md +330 -328
- package/docs/i18n.md +480 -476
- package/docs/installation.md +7 -6
- package/docs/llms.md +295 -295
- package/docs/state-management.md +289 -289
- package/guidelines/Guidelines.md +409 -406
- package/llms-compact.txt +1 -1
- package/llms.txt +1 -1
- package/package.json +219 -219
- package/styles/xertica/base.css +6 -0
- package/templates/.prettierignore +4 -4
- package/templates/.prettierrc +10 -10
- package/templates/CLAUDE.md +180 -165
- package/templates/guidelines/Guidelines.md +577 -553
- package/templates/package.json +69 -69
- package/templates/src/app/App.tsx +46 -46
- package/templates/src/app/components/AuthGuard.tsx +57 -8
- package/templates/src/features/assistant/data/mock.ts +75 -75
- package/templates/src/features/assistant/hooks/useAssistantConfig.ts +20 -20
- package/templates/src/features/assistant/index.ts +5 -5
- package/templates/src/features/auth/ui/AuthPageShell.tsx +1 -1
- package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +70 -72
- package/templates/src/features/auth/ui/LoginContent.tsx +92 -92
- package/templates/src/features/auth/ui/ResetPasswordContent.tsx +183 -179
- package/templates/src/features/auth/ui/SocialLoginButtons.tsx +78 -78
- package/templates/src/features/auth/ui/VerifyEmailContent.tsx +80 -84
- package/templates/src/features/home/data/mock.ts +6 -0
- package/templates/src/features/home/hooks/useFeatureCards.ts +20 -20
- package/templates/src/features/home/index.ts +11 -11
- package/templates/src/features/home/ui/HomeContent.tsx +117 -119
- package/templates/src/features/template/ui/CrudTemplate.tsx +112 -115
- package/templates/src/features/template/ui/DashboardTemplate.tsx +110 -110
- package/templates/src/features/template/ui/FormTemplate.tsx +117 -117
- package/templates/src/features/template/ui/LoginTemplate.tsx +59 -59
- package/templates/src/features/template/ui/TemplateContent.tsx +1322 -1314
- package/templates/src/i18n.ts +124 -124
- package/templates/src/locales/en/common.json +21 -21
- package/templates/src/locales/en/components/activityCard.json +10 -10
- package/templates/src/locales/en/components/assistant.json +119 -119
- package/templates/src/locales/en/components/media.json +29 -29
- package/templates/src/locales/en/components/notificationCard.json +5 -5
- package/templates/src/locales/en/components/profileCard.json +8 -8
- package/templates/src/locales/en/components/projectCard.json +10 -10
- package/templates/src/locales/en/components/sidebar.json +14 -14
- package/templates/src/locales/en/components/stats.json +8 -8
- package/templates/src/locales/en/components/team.json +14 -14
- package/templates/src/locales/en/errors.json +9 -9
- package/templates/src/locales/en/languageSelector.json +7 -7
- package/templates/src/locales/en/nav.json +6 -6
- package/templates/src/locales/en/pages/crudTemplate.json +25 -25
- package/templates/src/locales/en/pages/dashboardTemplate.json +20 -20
- package/templates/src/locales/en/pages/forgotPassword.json +10 -10
- package/templates/src/locales/en/pages/formTemplate.json +16 -16
- package/templates/src/locales/en/pages/home.json +7 -7
- package/templates/src/locales/en/pages/login.json +15 -15
- package/templates/src/locales/en/pages/loginTemplate.json +9 -9
- package/templates/src/locales/en/pages/resetPassword.json +18 -18
- package/templates/src/locales/en/pages/templates.json +317 -317
- package/templates/src/locales/en/pages/verifyEmail.json +12 -12
- package/templates/src/locales/en/themeToggle.json +6 -6
- package/templates/src/locales/es/common.json +21 -21
- package/templates/src/locales/es/components/activityCard.json +10 -10
- package/templates/src/locales/es/components/assistant.json +119 -119
- package/templates/src/locales/es/components/media.json +29 -29
- package/templates/src/locales/es/components/notificationCard.json +5 -5
- package/templates/src/locales/es/components/profileCard.json +8 -8
- package/templates/src/locales/es/components/projectCard.json +10 -10
- package/templates/src/locales/es/components/sidebar.json +14 -14
- package/templates/src/locales/es/components/stats.json +8 -8
- package/templates/src/locales/es/components/team.json +14 -14
- package/templates/src/locales/es/errors.json +9 -9
- package/templates/src/locales/es/languageSelector.json +7 -7
- package/templates/src/locales/es/nav.json +6 -6
- package/templates/src/locales/es/pages/crudTemplate.json +25 -25
- package/templates/src/locales/es/pages/dashboardTemplate.json +20 -20
- package/templates/src/locales/es/pages/forgotPassword.json +10 -10
- package/templates/src/locales/es/pages/formTemplate.json +16 -16
- package/templates/src/locales/es/pages/home.json +7 -7
- package/templates/src/locales/es/pages/login.json +15 -15
- package/templates/src/locales/es/pages/loginTemplate.json +9 -9
- package/templates/src/locales/es/pages/resetPassword.json +18 -18
- package/templates/src/locales/es/pages/templates.json +317 -317
- package/templates/src/locales/es/pages/verifyEmail.json +12 -12
- package/templates/src/locales/es/themeToggle.json +6 -6
- package/templates/src/locales/pt-BR/common.json +21 -21
- package/templates/src/locales/pt-BR/components/activityCard.json +10 -10
- package/templates/src/locales/pt-BR/components/assistant.json +119 -119
- package/templates/src/locales/pt-BR/components/media.json +29 -29
- package/templates/src/locales/pt-BR/components/notificationCard.json +5 -5
- package/templates/src/locales/pt-BR/components/profileCard.json +8 -8
- package/templates/src/locales/pt-BR/components/projectCard.json +10 -10
- package/templates/src/locales/pt-BR/components/sidebar.json +14 -14
- package/templates/src/locales/pt-BR/components/stats.json +8 -8
- package/templates/src/locales/pt-BR/components/team.json +14 -14
- package/templates/src/locales/pt-BR/errors.json +9 -9
- package/templates/src/locales/pt-BR/languageSelector.json +7 -7
- package/templates/src/locales/pt-BR/nav.json +6 -6
- package/templates/src/locales/pt-BR/pages/crudTemplate.json +25 -25
- package/templates/src/locales/pt-BR/pages/dashboardTemplate.json +20 -20
- package/templates/src/locales/pt-BR/pages/forgotPassword.json +10 -10
- package/templates/src/locales/pt-BR/pages/formTemplate.json +16 -16
- package/templates/src/locales/pt-BR/pages/home.json +7 -7
- package/templates/src/locales/pt-BR/pages/login.json +15 -15
- package/templates/src/locales/pt-BR/pages/loginTemplate.json +9 -9
- package/templates/src/locales/pt-BR/pages/resetPassword.json +18 -18
- package/templates/src/locales/pt-BR/pages/templates.json +317 -317
- package/templates/src/locales/pt-BR/pages/verifyEmail.json +12 -12
- package/templates/src/locales/pt-BR/themeToggle.json +6 -6
- package/templates/src/pages/AssistantPage.tsx +470 -464
- package/templates/src/pages/HomePage.tsx +53 -49
- package/templates/src/shared/error-boundary.tsx +1 -5
- package/templates/src/shared/error-fallbacks.tsx +4 -8
- package/templates/vite.config.js +20 -20
- package/templates/vite.config.ts +55 -52
- package/dist/AssistantChart-CxGjH7Qk.js +0 -3477
- package/dist/AssistantChart-DIpshm3i.js +0 -4784
- package/dist/AssistantChart-D_PTeu8P.cjs +0 -3503
- package/dist/AssistantChart-zjsy2GaZ.cjs +0 -4810
- package/dist/AudioPlayer-B1lt5cPl.cjs +0 -989
- package/dist/AudioPlayer-BZ7bibzU.cjs +0 -982
- package/dist/AudioPlayer-BpRPS4-1.cjs +0 -1277
- package/dist/AudioPlayer-C12BjQBV.cjs +0 -997
- package/dist/AudioPlayer-CFeV8t-5.cjs +0 -936
- package/dist/AudioPlayer-Coly3q5R.js +0 -1278
- package/dist/AudioPlayer-CySJIyvL.js +0 -937
- package/dist/AudioPlayer-DMcG_c7L.js +0 -990
- package/dist/AudioPlayer-DcFKRJE_.js +0 -998
- package/dist/AudioPlayer-e8LfNoqO.js +0 -983
- package/dist/BrandColorsContext-565dDHd5.js +0 -660
- package/dist/BrandColorsContext-BcJbtkqn.cjs +0 -659
- package/dist/CodeBlock-7TTgmdGG.cjs +0 -2094
- package/dist/CodeBlock-BlcqlA9M.cjs +0 -2094
- package/dist/CodeBlock-Bnmeu5ez.cjs +0 -2094
- package/dist/CodeBlock-BtfPlbAI.js +0 -2078
- package/dist/CodeBlock-CIySIuYr.js +0 -2078
- package/dist/CodeBlock-CuPtUM-7.cjs +0 -2094
- package/dist/CodeBlock-D6ffWXgc.js +0 -2078
- package/dist/CodeBlock-D8dcwbit.cjs +0 -2094
- package/dist/CodeBlock-DMZrFnlw.cjs +0 -2094
- package/dist/CodeBlock-DlBehYN8.js +0 -2078
- package/dist/CodeBlock-DnYNI8rQ.js +0 -2078
- package/dist/CodeBlock-DvKWbSnE.cjs +0 -2094
- package/dist/CodeBlock-DwMCfkFY.js +0 -2078
- package/dist/CodeBlock-Dy6CNYyj.js +0 -2078
- package/dist/CodeBlock-U1pPOQI7.cjs +0 -2094
- package/dist/CodeBlock-f_GpNhEB.js +0 -2078
- package/dist/CodeBlock-oB6u8nI1.js +0 -2078
- package/dist/CodeBlock-tZC31B73.cjs +0 -2094
- package/dist/FeatureCard-CxC-7C-C.cjs +0 -300
- package/dist/FeatureCard-DbHWCb4E.js +0 -301
- package/dist/ImageWithFallback-CGtidP6B.cjs +0 -4542
- package/dist/ImageWithFallback-lsg3pdFg.js +0 -4508
- package/dist/LanguageSelector-B5YfbHra.js +0 -231
- package/dist/LanguageSelector-D6uacAIM.cjs +0 -230
- package/dist/LayoutContext-B45-e9DI.cjs +0 -93
- package/dist/LayoutContext-BAql6ZRY.js +0 -97
- package/dist/LayoutContext-Bav3UMEA.js +0 -94
- package/dist/LayoutContext-BvK-ggDa.cjs +0 -96
- package/dist/ThemeContext-BoH4NLfN.js +0 -734
- package/dist/ThemeContext-r69W20Xg.cjs +0 -733
- package/dist/VerifyEmailPage-COiyNl1y.js +0 -2825
- package/dist/VerifyEmailPage-CqKsR2v8.js +0 -2827
- package/dist/VerifyEmailPage-DjQKRlUS.cjs +0 -2824
- package/dist/VerifyEmailPage-s-1X3LDJ.cjs +0 -2826
- package/dist/XerticaOrbe-KL1RBHzw.cjs +0 -1354
- package/dist/XerticaOrbe-zwS1p2a8.js +0 -1355
- package/dist/XerticaProvider-6btlAlzc.js +0 -17
- package/dist/XerticaProvider-BNoNOxQ5.cjs +0 -16
- package/dist/XerticaProvider-BlY2limY.cjs +0 -38
- package/dist/XerticaProvider-cI9hSs27.cjs +0 -38
- package/dist/XerticaProvider-hSwhNQex.js +0 -39
- package/dist/alert-dialog-BOje--vD.js +0 -847
- package/dist/alert-dialog-BtEuQqrg.cjs +0 -870
- package/dist/breadcrumb-CqJ7bHY5.js +0 -161
- package/dist/breadcrumb-m9Hb2_XN.cjs +0 -177
- package/dist/components/assistant/xertica-assistant/hooks/index.d.ts +0 -6
- package/dist/components/assistant/xertica-assistant/hooks/use-assistant-conversations.d.ts +0 -21
- package/dist/components/assistant/xertica-assistant/hooks/use-assistant-messages.d.ts +0 -49
- package/dist/components/assistant/xertica-assistant/hooks/use-assistant-suggestions.d.ts +0 -16
- package/dist/components/blocks/audio-player/AudioPlayer.d.ts +0 -35
- package/dist/components/blocks/audio-player/index.d.ts +0 -1
- package/dist/components/blocks/document-editor/DocumentEditor.d.ts +0 -26
- package/dist/components/blocks/document-editor/index.d.ts +0 -1
- package/dist/components/blocks/podcast-player/PodcastPlayer.d.ts +0 -41
- package/dist/components/blocks/podcast-player/index.d.ts +0 -1
- package/dist/components/ui/chart/parts/chart-dashboard.d.ts +0 -113
- package/dist/components/ui/chart/parts/chart-metric.d.ts +0 -118
- package/dist/components/ui/chart/parts/chart-primitives.d.ts +0 -101
- package/dist/components/ui/chart/parts/chart-shared.d.ts +0 -20
- package/dist/components/ui/chart/parts/chart-utils.d.ts +0 -12
- package/dist/components/ui/chart/parts/index.d.ts +0 -5
- package/dist/dropdown-menu-BDB5CmQs.cjs +0 -247
- package/dist/dropdown-menu-DQidbKBD.js +0 -231
- package/dist/google-maps-loader-BFWp6VPd.js +0 -287
- package/dist/google-maps-loader-BKcdgFbu.cjs +0 -312
- package/dist/google-maps-loader-CumCNXeG.js +0 -312
- package/dist/google-maps-loader-eS3uQ5TA.cjs +0 -287
- package/dist/header-Cgy6vYPk.cjs +0 -731
- package/dist/header-DRlT4jgI.js +0 -715
- package/dist/header-Dux00SI4.cjs +0 -731
- package/dist/header-EkGKXPsD.js +0 -715
- package/dist/header-WfEywpyc.cjs +0 -731
- package/dist/header-tifNQn2U.js +0 -715
- package/dist/index-BhapVLVj.js +0 -8
- package/dist/index-D6fxYEY8.cjs +0 -7
- package/dist/index-DAIp0_HK.js +0 -8
- package/dist/index-DW5tYe26.js +0 -8
- package/dist/index-GA__GvnG.cjs +0 -7
- package/dist/input-2R4loU86.js +0 -127
- package/dist/input-DWANSKGb.cjs +0 -145
- package/dist/progress-DPtzoVV8.js +0 -175
- package/dist/progress-EeaoqqUs.cjs +0 -191
- package/dist/rich-text-editor-0mraWT5y.cjs +0 -2376
- package/dist/rich-text-editor-B-IkcPD0.js +0 -2874
- package/dist/rich-text-editor-B6jMRLzk.cjs +0 -1939
- package/dist/rich-text-editor-B8_oYcIR.js +0 -1730
- package/dist/rich-text-editor-B9UbSXNb.js +0 -1203
- package/dist/rich-text-editor-BYuRBNBU.js +0 -2373
- package/dist/rich-text-editor-Bb9pySTs.cjs +0 -2374
- package/dist/rich-text-editor-BcL6L3cm.cjs +0 -2374
- package/dist/rich-text-editor-BoVZYtTs.cjs +0 -2391
- package/dist/rich-text-editor-Bp3zQqMC.js +0 -2954
- package/dist/rich-text-editor-CMgSN_w2.js +0 -1189
- package/dist/rich-text-editor-CPV1lEPH.cjs +0 -1748
- package/dist/rich-text-editor-CeucBdIv.cjs +0 -2971
- package/dist/rich-text-editor-CoKqbCtu.cjs +0 -1799
- package/dist/rich-text-editor-Cw56T_mB.js +0 -2356
- package/dist/rich-text-editor-Cyt8qs2b.js +0 -1921
- package/dist/rich-text-editor-D6H84OcX.cjs +0 -1220
- package/dist/rich-text-editor-D76gD-QI.js +0 -2328
- package/dist/rich-text-editor-DKkokOnA.js +0 -1781
- package/dist/rich-text-editor-DNsdpN64.cjs +0 -2359
- package/dist/rich-text-editor-DfG8bCyY.js +0 -2358
- package/dist/rich-text-editor-Dxjw31Z4.js +0 -2341
- package/dist/rich-text-editor-DzP0Epmb.js +0 -2356
- package/dist/rich-text-editor-bRkNoeZY.cjs +0 -2891
- package/dist/rich-text-editor-lyYE2ZG5.cjs +0 -1207
- package/dist/rich-text-editor-skplNlBM.cjs +0 -2345
- package/dist/select-Bkbr0f-Z.cjs +0 -162
- package/dist/select-CvIVdX2n.js +0 -145
- package/dist/sidebar-CK_0ZQHj.cjs +0 -803
- package/dist/sidebar-CUuOvYhK.js +0 -787
- package/dist/sidebar-Djn5syhi.cjs +0 -786
- package/dist/sidebar-LluMXfam.js +0 -759
- package/dist/sidebar-_rT7rBMk.js +0 -787
- package/dist/slider-Bc5Hd0y1.js +0 -56
- package/dist/slider-N7hFFj6X.cjs +0 -73
- package/dist/tooltip-Ded96neP.cjs +0 -137
- package/dist/tooltip-HDOoD2-0.js +0 -120
- package/dist/use-audio-player-B31J-aqh.cjs +0 -187
- package/dist/use-audio-player-BkmEmj8Q.js +0 -185
- package/dist/use-audio-player-CLFTWFW1.cjs +0 -184
- package/dist/use-audio-player-CLLn00I6.js +0 -188
- package/dist/use-file-upload-BcjEo2S5.js +0 -404
- package/dist/use-file-upload-CRJR68Tj.cjs +0 -403
- package/dist/use-mobile-B0hNy_Y6.cjs +0 -4303
- package/dist/use-mobile-BXuYROXM.js +0 -4202
- package/dist/use-mobile-Bbd51ASU.cjs +0 -4392
- package/dist/use-mobile-Bk6CX-TC.js +0 -4359
- package/dist/use-mobile-BvYdisLP.js +0 -4202
- package/dist/use-mobile-BzuxjzNX.cjs +0 -4392
- package/dist/use-mobile-CG2-SdXV.cjs +0 -4235
- package/dist/use-mobile-CKb5pqTs.js +0 -4269
- package/dist/use-mobile-CYuAuGDl.js +0 -4202
- package/dist/use-mobile-CaENcqm-.js +0 -4508
- package/dist/use-mobile-CbrYgJGJ.js +0 -4203
- package/dist/use-mobile-Cd4xPrKq.cjs +0 -46
- package/dist/use-mobile-DMOvImGQ.cjs +0 -4542
- package/dist/use-mobile-DRB3BQgD.cjs +0 -4235
- package/dist/use-mobile-DZvv7QMR.js +0 -4359
- package/dist/use-mobile-DdI_TXam.cjs +0 -4235
- package/dist/use-mobile-DlceKf8a.js +0 -4359
- package/dist/use-mobile-DsOnow1o.cjs +0 -4236
- package/dist/use-mobile-Kcj6jSnK.cjs +0 -4392
- package/dist/use-mobile-bnKcua_i.js +0 -4202
- package/dist/use-mobile-j4w2Jrf1.js +0 -30
- package/dist/use-mobile-ncXBeE2z.cjs +0 -4235
- package/dist/use-rich-text-editor-DjiddBGv.js +0 -282
- package/dist/use-rich-text-editor-lpeswbCs.cjs +0 -281
- package/dist/xertica-assistant-BdiZag0h.js +0 -2187
- package/dist/xertica-assistant-DUBpmEgo.cjs +0 -2186
- package/dist/{rich-text-editor-DgF8s7xW.js → rich-text-editor-BmsjY03B.js} +26 -26
- package/dist/{rich-text-editor-mWoaSCE4.cjs → rich-text-editor-GS2kpTAK.cjs} +26 -26
|
@@ -1,554 +1,564 @@
|
|
|
1
|
-
import React, { useRef, useEffect, useState } from 'react';
|
|
2
|
-
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
-
import { Send, Plus, Paperclip, Mic, FileText, Radio, Search, X } from 'lucide-react';
|
|
4
|
-
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import { Button } from '../../ui/button';
|
|
6
|
-
import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover';
|
|
7
|
-
import { toast } from 'sonner';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Supported special action types for the chat input.
|
|
11
|
-
* - 'document': Triggers document generation workflow.
|
|
12
|
-
* - 'podcast': Triggers audio podcast generation.
|
|
13
|
-
* - 'search': Triggers a knowledge base search.
|
|
14
|
-
*/
|
|
15
|
-
export type ActionType = 'document' | 'podcast' | 'search' | null;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Props for the ModernChatInput component.
|
|
19
|
-
*/
|
|
20
|
-
interface ModernChatInputProps {
|
|
21
|
-
/** Current input text value */
|
|
22
|
-
value: string;
|
|
23
|
-
/** Callback fired on every keystroke */
|
|
24
|
-
onChange: (value: string) => void;
|
|
25
|
-
/** Callback fired when the user submits (via Enter or click) */
|
|
26
|
-
onSubmit: (action?: ActionType) => void;
|
|
27
|
-
/** Input placeholder text */
|
|
28
|
-
placeholder?: string;
|
|
29
|
-
/** Whether the input is disabled */
|
|
30
|
-
disabled?: boolean;
|
|
31
|
-
/** Callback for file attachment button */
|
|
32
|
-
onFileUpload?: () => void;
|
|
33
|
-
/** Callback for audio upload button */
|
|
34
|
-
onAudioUpload?: () => void;
|
|
35
|
-
/** Callback fired when a voice recording is finished and transcribed */
|
|
36
|
-
onVoiceRecording?: (transcript: string) => void;
|
|
37
|
-
/** Whether the input is being used in a full-page view (affects layout) */
|
|
38
|
-
isFullPage?: boolean;
|
|
39
|
-
/** Whether to show audio input (voice recording). @default true */
|
|
40
|
-
enableAudioInput?: boolean;
|
|
41
|
-
/** Whether to show the file attachment button. @default true */
|
|
42
|
-
enableFileAttachment?: boolean;
|
|
43
|
-
/** Whether to show the "Create document" action. @default true */
|
|
44
|
-
enableDocumentCreation?: boolean;
|
|
45
|
-
/** Whether to show the "Generate podcast" action. @default true */
|
|
46
|
-
enablePodcastGeneration?: boolean;
|
|
47
|
-
/** Whether to show the "Search" action. @default true */
|
|
48
|
-
enableSearch?: boolean;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Modern floating chat input with rich actions and voice recording.
|
|
53
|
-
*
|
|
54
|
-
* @description
|
|
55
|
-
* An advanced text input that supports multi-line typing, quick action chips
|
|
56
|
-
* (Document, Podcast, Search), file attachments, and simulated voice-to-text recording.
|
|
57
|
-
*
|
|
58
|
-
* @ai-rules
|
|
59
|
-
* 1. Use the `onSubmit` callback to handle both text messages and special actions.
|
|
60
|
-
* 2. This component manages its own height dynamically based on content (up to 100px).
|
|
61
|
-
* 3. Voice recording is simulated — it returns a random string to the `onVoiceRecording` callback.
|
|
62
|
-
*/
|
|
63
|
-
export function ModernChatInput({
|
|
64
|
-
value,
|
|
65
|
-
onChange,
|
|
66
|
-
onSubmit,
|
|
67
|
-
placeholder,
|
|
68
|
-
disabled = false,
|
|
69
|
-
onFileUpload,
|
|
70
|
-
onAudioUpload,
|
|
71
|
-
onVoiceRecording,
|
|
72
|
-
isFullPage = false,
|
|
73
|
-
enableAudioInput = true,
|
|
74
|
-
enableFileAttachment = true,
|
|
75
|
-
enableDocumentCreation = true,
|
|
76
|
-
enablePodcastGeneration = true,
|
|
77
|
-
enableSearch = true,
|
|
78
|
-
}: ModernChatInputProps) {
|
|
79
|
-
const { t } = useTranslation();
|
|
80
|
-
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
81
|
-
const [isFocused, setIsFocused] = useState(false);
|
|
82
|
-
const [textareaHeight, setTextareaHeight] = useState(20);
|
|
83
|
-
const [selectedAction, setSelectedAction] = useState<ActionType>(null);
|
|
84
|
-
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
85
|
-
const [isRecording, setIsRecording] = useState(false);
|
|
86
|
-
const [recordingTime, setRecordingTime] = useState(0);
|
|
87
|
-
const recordingIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
88
|
-
|
|
89
|
-
// Resolve placeholder: caller prop takes precedence, then i18n key
|
|
90
|
-
const resolvedPlaceholder = placeholder ?? t('assistant.inputPlaceholder');
|
|
91
|
-
|
|
92
|
-
const adjustHeight = () => {
|
|
93
|
-
const textarea = textareaRef.current;
|
|
94
|
-
if (textarea) {
|
|
95
|
-
// Reset to minimum height first
|
|
96
|
-
textarea.style.height = '20px';
|
|
97
|
-
|
|
98
|
-
// Calculate new height based on content
|
|
99
|
-
const scrollHeight = textarea.scrollHeight;
|
|
100
|
-
const newHeight = Math.min(Math.max(scrollHeight, 20), 100);
|
|
101
|
-
|
|
102
|
-
// Apply the new height smoothly
|
|
103
|
-
setTextareaHeight(newHeight);
|
|
104
|
-
textarea.style.height = newHeight + 'px';
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
// Initialize with proper height on mount
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
const textarea = textareaRef.current;
|
|
111
|
-
if (textarea) {
|
|
112
|
-
// Force initial height without content check
|
|
113
|
-
textarea.style.height = '20px';
|
|
114
|
-
setTextareaHeight(20);
|
|
115
|
-
|
|
116
|
-
// If there's initial content, adjust immediately
|
|
117
|
-
if (value) {
|
|
118
|
-
setTimeout(adjustHeight, 0);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}, []); // Empty dependency array - only runs on mount
|
|
122
|
-
|
|
123
|
-
// Adjust height when value changes
|
|
124
|
-
useEffect(() => {
|
|
125
|
-
if (value === '') {
|
|
126
|
-
// Reset to minimum when empty
|
|
127
|
-
const textarea = textareaRef.current;
|
|
128
|
-
if (textarea) {
|
|
129
|
-
textarea.style.height = '20px';
|
|
130
|
-
setTextareaHeight(20);
|
|
131
|
-
}
|
|
132
|
-
} else {
|
|
133
|
-
// Adjust for content
|
|
134
|
-
const timeoutId = setTimeout(adjustHeight, 0);
|
|
135
|
-
return () => clearTimeout(timeoutId);
|
|
136
|
-
}
|
|
137
|
-
}, [value]);
|
|
138
|
-
|
|
139
|
-
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
140
|
-
if (e.key === 'Enter' && !e.shiftKey) {
|
|
141
|
-
e.preventDefault();
|
|
142
|
-
if (value.trim() && !disabled) {
|
|
143
|
-
onSubmit(selectedAction);
|
|
144
|
-
setSelectedAction(null);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const handleActionSelect = (action: ActionType) => {
|
|
150
|
-
setSelectedAction(action);
|
|
151
|
-
setIsPopoverOpen(false);
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const handleRemoveAction = () => {
|
|
155
|
-
setSelectedAction(null);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const handleVoiceRecording = () => {
|
|
159
|
-
if (isRecording) {
|
|
160
|
-
// Stop recording
|
|
161
|
-
if (recordingIntervalRef.current) {
|
|
162
|
-
clearInterval(recordingIntervalRef.current);
|
|
163
|
-
recordingIntervalRef.current = null;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Simulate audio transcription — demo strings keyed by i18n
|
|
167
|
-
const transcricoes = [
|
|
168
|
-
t('assistant.voiceTranscriptions.salesData'),
|
|
169
|
-
t('assistant.voiceTranscriptions.performanceReport'),
|
|
170
|
-
t('assistant.voiceTranscriptions.marketTrends'),
|
|
171
|
-
t('assistant.voiceTranscriptions.customerSatisfaction'),
|
|
172
|
-
t('assistant.voiceTranscriptions.teamProductivity'),
|
|
173
|
-
t('assistant.voiceTranscriptions.keyMetrics'),
|
|
174
|
-
t('assistant.voiceTranscriptions.digitalMarketing'),
|
|
175
|
-
t('assistant.voiceTranscriptions.socialEngagement'),
|
|
176
|
-
];
|
|
177
|
-
|
|
178
|
-
const transcricaoAleatoria = transcricoes[Math.floor(Math.random() * transcricoes.length)];
|
|
179
|
-
|
|
180
|
-
// Call the callback with the transcription
|
|
181
|
-
if (onVoiceRecording) {
|
|
182
|
-
onVoiceRecording(transcricaoAleatoria);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
setIsRecording(false);
|
|
186
|
-
setRecordingTime(0);
|
|
187
|
-
} else {
|
|
188
|
-
// Start recording
|
|
189
|
-
setIsRecording(true);
|
|
190
|
-
setRecordingTime(0);
|
|
191
|
-
|
|
192
|
-
toast.info(t('assistant.recordingStarted'), {
|
|
193
|
-
description: t('assistant.recordingDescriptionFull'),
|
|
194
|
-
duration: 3000,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
recordingIntervalRef.current = setInterval(() => {
|
|
198
|
-
setRecordingTime(prev => prev + 1);
|
|
199
|
-
}, 1000);
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
// Automatically stop recording after 60 seconds
|
|
204
|
-
useEffect(() => {
|
|
205
|
-
if (recordingTime >= 60 && isRecording) {
|
|
206
|
-
handleVoiceRecording();
|
|
207
|
-
}
|
|
208
|
-
}, [recordingTime, isRecording]);
|
|
209
|
-
|
|
210
|
-
// Clear interval on unmount
|
|
211
|
-
useEffect(() => {
|
|
212
|
-
return () => {
|
|
213
|
-
if (recordingIntervalRef.current) {
|
|
214
|
-
clearInterval(recordingIntervalRef.current);
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
}, []);
|
|
218
|
-
|
|
219
|
-
const getActionInfo = (action: ActionType) => {
|
|
220
|
-
switch (action) {
|
|
221
|
-
case 'document':
|
|
222
|
-
return {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
<
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
>
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
</
|
|
551
|
-
</motion.div>
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
1
|
+
import React, { useRef, useEffect, useState } from 'react';
|
|
2
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
3
|
+
import { Send, Plus, Paperclip, Mic, FileText, Radio, Search, X } from 'lucide-react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { Button } from '../../ui/button';
|
|
6
|
+
import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover';
|
|
7
|
+
import { toast } from 'sonner';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Supported special action types for the chat input.
|
|
11
|
+
* - 'document': Triggers document generation workflow.
|
|
12
|
+
* - 'podcast': Triggers audio podcast generation.
|
|
13
|
+
* - 'search': Triggers a knowledge base search.
|
|
14
|
+
*/
|
|
15
|
+
export type ActionType = 'document' | 'podcast' | 'search' | null;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Props for the ModernChatInput component.
|
|
19
|
+
*/
|
|
20
|
+
interface ModernChatInputProps {
|
|
21
|
+
/** Current input text value */
|
|
22
|
+
value: string;
|
|
23
|
+
/** Callback fired on every keystroke */
|
|
24
|
+
onChange: (value: string) => void;
|
|
25
|
+
/** Callback fired when the user submits (via Enter or click) */
|
|
26
|
+
onSubmit: (action?: ActionType) => void;
|
|
27
|
+
/** Input placeholder text */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
/** Whether the input is disabled */
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
/** Callback for file attachment button */
|
|
32
|
+
onFileUpload?: () => void;
|
|
33
|
+
/** Callback for audio upload button */
|
|
34
|
+
onAudioUpload?: () => void;
|
|
35
|
+
/** Callback fired when a voice recording is finished and transcribed */
|
|
36
|
+
onVoiceRecording?: (transcript: string) => void;
|
|
37
|
+
/** Whether the input is being used in a full-page view (affects layout) */
|
|
38
|
+
isFullPage?: boolean;
|
|
39
|
+
/** Whether to show audio input (voice recording). @default true */
|
|
40
|
+
enableAudioInput?: boolean;
|
|
41
|
+
/** Whether to show the file attachment button. @default true */
|
|
42
|
+
enableFileAttachment?: boolean;
|
|
43
|
+
/** Whether to show the "Create document" action. @default true */
|
|
44
|
+
enableDocumentCreation?: boolean;
|
|
45
|
+
/** Whether to show the "Generate podcast" action. @default true */
|
|
46
|
+
enablePodcastGeneration?: boolean;
|
|
47
|
+
/** Whether to show the "Search" action. @default true */
|
|
48
|
+
enableSearch?: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Modern floating chat input with rich actions and voice recording.
|
|
53
|
+
*
|
|
54
|
+
* @description
|
|
55
|
+
* An advanced text input that supports multi-line typing, quick action chips
|
|
56
|
+
* (Document, Podcast, Search), file attachments, and simulated voice-to-text recording.
|
|
57
|
+
*
|
|
58
|
+
* @ai-rules
|
|
59
|
+
* 1. Use the `onSubmit` callback to handle both text messages and special actions.
|
|
60
|
+
* 2. This component manages its own height dynamically based on content (up to 100px).
|
|
61
|
+
* 3. Voice recording is simulated — it returns a random string to the `onVoiceRecording` callback.
|
|
62
|
+
*/
|
|
63
|
+
export function ModernChatInput({
|
|
64
|
+
value,
|
|
65
|
+
onChange,
|
|
66
|
+
onSubmit,
|
|
67
|
+
placeholder,
|
|
68
|
+
disabled = false,
|
|
69
|
+
onFileUpload,
|
|
70
|
+
onAudioUpload,
|
|
71
|
+
onVoiceRecording,
|
|
72
|
+
isFullPage = false,
|
|
73
|
+
enableAudioInput = true,
|
|
74
|
+
enableFileAttachment = true,
|
|
75
|
+
enableDocumentCreation = true,
|
|
76
|
+
enablePodcastGeneration = true,
|
|
77
|
+
enableSearch = true,
|
|
78
|
+
}: ModernChatInputProps) {
|
|
79
|
+
const { t } = useTranslation();
|
|
80
|
+
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
|
81
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
82
|
+
const [textareaHeight, setTextareaHeight] = useState(20);
|
|
83
|
+
const [selectedAction, setSelectedAction] = useState<ActionType>(null);
|
|
84
|
+
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
85
|
+
const [isRecording, setIsRecording] = useState(false);
|
|
86
|
+
const [recordingTime, setRecordingTime] = useState(0);
|
|
87
|
+
const recordingIntervalRef = useRef<NodeJS.Timeout | null>(null);
|
|
88
|
+
|
|
89
|
+
// Resolve placeholder: caller prop takes precedence, then i18n key
|
|
90
|
+
const resolvedPlaceholder = placeholder ?? t('assistant.inputPlaceholder');
|
|
91
|
+
|
|
92
|
+
const adjustHeight = () => {
|
|
93
|
+
const textarea = textareaRef.current;
|
|
94
|
+
if (textarea) {
|
|
95
|
+
// Reset to minimum height first
|
|
96
|
+
textarea.style.height = '20px';
|
|
97
|
+
|
|
98
|
+
// Calculate new height based on content
|
|
99
|
+
const scrollHeight = textarea.scrollHeight;
|
|
100
|
+
const newHeight = Math.min(Math.max(scrollHeight, 20), 100);
|
|
101
|
+
|
|
102
|
+
// Apply the new height smoothly
|
|
103
|
+
setTextareaHeight(newHeight);
|
|
104
|
+
textarea.style.height = newHeight + 'px';
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Initialize with proper height on mount
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
const textarea = textareaRef.current;
|
|
111
|
+
if (textarea) {
|
|
112
|
+
// Force initial height without content check
|
|
113
|
+
textarea.style.height = '20px';
|
|
114
|
+
setTextareaHeight(20);
|
|
115
|
+
|
|
116
|
+
// If there's initial content, adjust immediately
|
|
117
|
+
if (value) {
|
|
118
|
+
setTimeout(adjustHeight, 0);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}, []); // Empty dependency array - only runs on mount
|
|
122
|
+
|
|
123
|
+
// Adjust height when value changes
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (value === '') {
|
|
126
|
+
// Reset to minimum when empty
|
|
127
|
+
const textarea = textareaRef.current;
|
|
128
|
+
if (textarea) {
|
|
129
|
+
textarea.style.height = '20px';
|
|
130
|
+
setTextareaHeight(20);
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
// Adjust for content
|
|
134
|
+
const timeoutId = setTimeout(adjustHeight, 0);
|
|
135
|
+
return () => clearTimeout(timeoutId);
|
|
136
|
+
}
|
|
137
|
+
}, [value]);
|
|
138
|
+
|
|
139
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
140
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
141
|
+
e.preventDefault();
|
|
142
|
+
if (value.trim() && !disabled) {
|
|
143
|
+
onSubmit(selectedAction);
|
|
144
|
+
setSelectedAction(null);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const handleActionSelect = (action: ActionType) => {
|
|
150
|
+
setSelectedAction(action);
|
|
151
|
+
setIsPopoverOpen(false);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handleRemoveAction = () => {
|
|
155
|
+
setSelectedAction(null);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const handleVoiceRecording = () => {
|
|
159
|
+
if (isRecording) {
|
|
160
|
+
// Stop recording
|
|
161
|
+
if (recordingIntervalRef.current) {
|
|
162
|
+
clearInterval(recordingIntervalRef.current);
|
|
163
|
+
recordingIntervalRef.current = null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Simulate audio transcription — demo strings keyed by i18n
|
|
167
|
+
const transcricoes = [
|
|
168
|
+
t('assistant.voiceTranscriptions.salesData'),
|
|
169
|
+
t('assistant.voiceTranscriptions.performanceReport'),
|
|
170
|
+
t('assistant.voiceTranscriptions.marketTrends'),
|
|
171
|
+
t('assistant.voiceTranscriptions.customerSatisfaction'),
|
|
172
|
+
t('assistant.voiceTranscriptions.teamProductivity'),
|
|
173
|
+
t('assistant.voiceTranscriptions.keyMetrics'),
|
|
174
|
+
t('assistant.voiceTranscriptions.digitalMarketing'),
|
|
175
|
+
t('assistant.voiceTranscriptions.socialEngagement'),
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
const transcricaoAleatoria = transcricoes[Math.floor(Math.random() * transcricoes.length)];
|
|
179
|
+
|
|
180
|
+
// Call the callback with the transcription
|
|
181
|
+
if (onVoiceRecording) {
|
|
182
|
+
onVoiceRecording(transcricaoAleatoria);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
setIsRecording(false);
|
|
186
|
+
setRecordingTime(0);
|
|
187
|
+
} else {
|
|
188
|
+
// Start recording
|
|
189
|
+
setIsRecording(true);
|
|
190
|
+
setRecordingTime(0);
|
|
191
|
+
|
|
192
|
+
toast.info(t('assistant.recordingStarted'), {
|
|
193
|
+
description: t('assistant.recordingDescriptionFull'),
|
|
194
|
+
duration: 3000,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
recordingIntervalRef.current = setInterval(() => {
|
|
198
|
+
setRecordingTime(prev => prev + 1);
|
|
199
|
+
}, 1000);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// Automatically stop recording after 60 seconds
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (recordingTime >= 60 && isRecording) {
|
|
206
|
+
handleVoiceRecording();
|
|
207
|
+
}
|
|
208
|
+
}, [recordingTime, isRecording]);
|
|
209
|
+
|
|
210
|
+
// Clear interval on unmount
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
return () => {
|
|
213
|
+
if (recordingIntervalRef.current) {
|
|
214
|
+
clearInterval(recordingIntervalRef.current);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
}, []);
|
|
218
|
+
|
|
219
|
+
const getActionInfo = (action: ActionType) => {
|
|
220
|
+
switch (action) {
|
|
221
|
+
case 'document':
|
|
222
|
+
return {
|
|
223
|
+
label: t('assistant.actions.createDocument'),
|
|
224
|
+
icon: FileText,
|
|
225
|
+
color: 'bg-[var(--chart-4)]',
|
|
226
|
+
};
|
|
227
|
+
case 'podcast':
|
|
228
|
+
return {
|
|
229
|
+
label: t('assistant.actions.generatePodcast'),
|
|
230
|
+
icon: Radio,
|
|
231
|
+
color: 'bg-[var(--chart-1)]',
|
|
232
|
+
};
|
|
233
|
+
case 'search':
|
|
234
|
+
return { label: t('assistant.actions.search'), icon: Search, color: 'bg-[var(--chart-2)]' };
|
|
235
|
+
default:
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const handleFocus = () => setIsFocused(true);
|
|
241
|
+
const handleBlur = () => setIsFocused(false);
|
|
242
|
+
|
|
243
|
+
const hasContent = value.trim().length > 0;
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<div className={`p-4 ${isFullPage ? 'mx-auto w-full max-w-6xl' : ''}`}>
|
|
247
|
+
{/* Recording Indicator */}
|
|
248
|
+
<AnimatePresence>
|
|
249
|
+
{isRecording && (
|
|
250
|
+
<motion.div
|
|
251
|
+
initial={{ opacity: 0, y: 10 }}
|
|
252
|
+
animate={{ opacity: 1, y: 0 }}
|
|
253
|
+
exit={{ opacity: 0, y: 10 }}
|
|
254
|
+
className="mb-3 p-4 bg-destructive/10 border border-destructive/30 rounded-[var(--radius-card)]"
|
|
255
|
+
>
|
|
256
|
+
<div className="flex items-center justify-between">
|
|
257
|
+
<div className="flex items-center gap-3">
|
|
258
|
+
{/* Pulsing Dot */}
|
|
259
|
+
<motion.div
|
|
260
|
+
animate={{
|
|
261
|
+
scale: [1, 1.2, 1],
|
|
262
|
+
opacity: [1, 0.7, 1],
|
|
263
|
+
}}
|
|
264
|
+
transition={{
|
|
265
|
+
duration: 1.5,
|
|
266
|
+
repeat: Infinity,
|
|
267
|
+
ease: 'easeInOut',
|
|
268
|
+
}}
|
|
269
|
+
className="w-3 h-3 bg-destructive rounded-full"
|
|
270
|
+
/>
|
|
271
|
+
|
|
272
|
+
{/* Audio Waves Animation */}
|
|
273
|
+
<div className="flex items-center gap-1">
|
|
274
|
+
{[0, 1, 2, 3, 4].map(i => (
|
|
275
|
+
<motion.div
|
|
276
|
+
key={i}
|
|
277
|
+
animate={{
|
|
278
|
+
height: ['8px', '20px', '8px'],
|
|
279
|
+
}}
|
|
280
|
+
transition={{
|
|
281
|
+
duration: 0.8,
|
|
282
|
+
repeat: Infinity,
|
|
283
|
+
ease: 'easeInOut',
|
|
284
|
+
delay: i * 0.1,
|
|
285
|
+
}}
|
|
286
|
+
className="w-1 bg-destructive rounded-full h-2"
|
|
287
|
+
/>
|
|
288
|
+
))}
|
|
289
|
+
</div>
|
|
290
|
+
|
|
291
|
+
<span className="text-sm font-medium text-destructive">
|
|
292
|
+
{t('assistant.recordingAudio')}
|
|
293
|
+
</span>
|
|
294
|
+
</div>
|
|
295
|
+
|
|
296
|
+
<div className="flex items-center gap-3">
|
|
297
|
+
<div className="text-sm text-destructive font-mono tabular-nums">
|
|
298
|
+
{Math.floor(recordingTime / 60)}:
|
|
299
|
+
{(recordingTime % 60).toString().padStart(2, '0')}
|
|
300
|
+
</div>
|
|
301
|
+
<Button
|
|
302
|
+
onClick={handleVoiceRecording}
|
|
303
|
+
size="sm"
|
|
304
|
+
variant="outline"
|
|
305
|
+
className="h-7 px-3 text-xs border-destructive/30 text-destructive hover:bg-destructive/10 rounded-[var(--radius-button)]"
|
|
306
|
+
>
|
|
307
|
+
{t('assistant.stopRecording')}
|
|
308
|
+
</Button>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</motion.div>
|
|
312
|
+
)}
|
|
313
|
+
</AnimatePresence>
|
|
314
|
+
|
|
315
|
+
{/* Modern Input Container */}
|
|
316
|
+
<motion.div
|
|
317
|
+
className={`relative bg-card rounded-[var(--radius)] border shadow-sm transition-all duration-300 ${
|
|
318
|
+
isFocused || hasContent ? 'border-primary shadow-lg shadow-primary/10' : 'border-border'
|
|
319
|
+
}`}
|
|
320
|
+
initial={false}
|
|
321
|
+
animate={{
|
|
322
|
+
scale: isFocused ? 1.01 : 1,
|
|
323
|
+
}}
|
|
324
|
+
transition={{ duration: 0.2, ease: 'easeOut' }}
|
|
325
|
+
>
|
|
326
|
+
<div className="px-3 py-2 pt-[14px] pr-[10px] pb-[8px] pl-[10px] rounded-[var(--radius)]">
|
|
327
|
+
{/* Action Chip */}
|
|
328
|
+
<AnimatePresence>
|
|
329
|
+
{selectedAction && (
|
|
330
|
+
<motion.div
|
|
331
|
+
initial={{ opacity: 0, y: -10, height: 0 }}
|
|
332
|
+
animate={{ opacity: 1, y: 0, height: 'auto' }}
|
|
333
|
+
exit={{ opacity: 0, y: -10, height: 0 }}
|
|
334
|
+
transition={{ duration: 0.2 }}
|
|
335
|
+
className="mb-2"
|
|
336
|
+
>
|
|
337
|
+
{(() => {
|
|
338
|
+
const actionInfo = getActionInfo(selectedAction);
|
|
339
|
+
if (!actionInfo) return null;
|
|
340
|
+
const Icon = actionInfo.icon;
|
|
341
|
+
return (
|
|
342
|
+
<div
|
|
343
|
+
className={`inline-flex items-center gap-2 px-3 py-1.5 rounded-full ${actionInfo.color} text-white text-xs font-medium shadow-sm`}
|
|
344
|
+
>
|
|
345
|
+
<Icon className="w-3.5 h-3.5" />
|
|
346
|
+
<span>{actionInfo.label}</span>
|
|
347
|
+
<button
|
|
348
|
+
onClick={handleRemoveAction}
|
|
349
|
+
className="ml-1 hover:bg-white/20 rounded-full p-0.5 transition-colors"
|
|
350
|
+
aria-label={t('assistant.removeAction')}
|
|
351
|
+
>
|
|
352
|
+
<X className="w-3 h-3" />
|
|
353
|
+
</button>
|
|
354
|
+
</div>
|
|
355
|
+
);
|
|
356
|
+
})()}
|
|
357
|
+
</motion.div>
|
|
358
|
+
)}
|
|
359
|
+
</AnimatePresence>
|
|
360
|
+
|
|
361
|
+
{/* Text Input - Full Width */}
|
|
362
|
+
<div className="mb-2">
|
|
363
|
+
<textarea
|
|
364
|
+
ref={textareaRef}
|
|
365
|
+
value={value}
|
|
366
|
+
onChange={e => onChange(e.target.value)}
|
|
367
|
+
onKeyDown={handleKeyDown}
|
|
368
|
+
onFocus={handleFocus}
|
|
369
|
+
onBlur={handleBlur}
|
|
370
|
+
placeholder={resolvedPlaceholder}
|
|
371
|
+
aria-label={resolvedPlaceholder}
|
|
372
|
+
disabled={disabled}
|
|
373
|
+
className="w-full bg-transparent border-0 outline-none resize-none text-foreground placeholder-muted-foreground text-sm leading-5 min-h-[20px] max-h-[100px] overflow-y-auto scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent"
|
|
374
|
+
style={{
|
|
375
|
+
height: `${textareaHeight}px`,
|
|
376
|
+
minHeight: '20px',
|
|
377
|
+
maxHeight: '100px',
|
|
378
|
+
}}
|
|
379
|
+
rows={1}
|
|
380
|
+
/>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
{/* Actions Row - Bottom */}
|
|
384
|
+
<div className="flex items-center justify-between">
|
|
385
|
+
{/* Left Actions */}
|
|
386
|
+
<div className="flex items-center gap-1">
|
|
387
|
+
{(enableDocumentCreation || enablePodcastGeneration || enableSearch) && (
|
|
388
|
+
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
|
389
|
+
<PopoverTrigger asChild>
|
|
390
|
+
<Button
|
|
391
|
+
variant="ghost"
|
|
392
|
+
size="sm"
|
|
393
|
+
className="h-8 w-8 p-0 rounded-full text-muted-foreground hover:bg-accent hover:text-foreground transition-all duration-200"
|
|
394
|
+
aria-label={t('assistant.viewActions')}
|
|
395
|
+
>
|
|
396
|
+
<motion.div
|
|
397
|
+
whileHover={{ scale: 1.05 }}
|
|
398
|
+
whileTap={{ scale: 0.95 }}
|
|
399
|
+
className="flex items-center justify-center w-full h-full"
|
|
400
|
+
>
|
|
401
|
+
<Plus className="w-4 h-4" />
|
|
402
|
+
</motion.div>
|
|
403
|
+
</Button>
|
|
404
|
+
</PopoverTrigger>
|
|
405
|
+
<PopoverContent className="w-56 p-2" align="start" side="top">
|
|
406
|
+
<div className="space-y-1">
|
|
407
|
+
{enableDocumentCreation && (
|
|
408
|
+
<button
|
|
409
|
+
onClick={() => handleActionSelect('document')}
|
|
410
|
+
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-accent transition-colors text-left group"
|
|
411
|
+
>
|
|
412
|
+
<div className="w-8 h-8 rounded-full bg-[var(--chart-4)]/20 flex items-center justify-center group-hover:bg-[var(--chart-4)] transition-colors">
|
|
413
|
+
<FileText className="w-4 h-4 text-[var(--chart-4)] group-hover:text-white" />
|
|
414
|
+
</div>
|
|
415
|
+
<div className="flex-1">
|
|
416
|
+
<div>{t('assistant.actions.createDocument')}</div>
|
|
417
|
+
<div className="text-xs text-muted-foreground">
|
|
418
|
+
{t('assistant.actions.createDocumentDesc')}
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
</button>
|
|
422
|
+
)}
|
|
423
|
+
|
|
424
|
+
{enablePodcastGeneration && (
|
|
425
|
+
<button
|
|
426
|
+
onClick={() => handleActionSelect('podcast')}
|
|
427
|
+
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-accent transition-colors text-left group"
|
|
428
|
+
>
|
|
429
|
+
<div className="w-8 h-8 rounded-full bg-[var(--chart-1)]/20 flex items-center justify-center group-hover:bg-[var(--chart-1)] transition-colors">
|
|
430
|
+
<Radio className="w-4 h-4 text-[var(--chart-1)] group-hover:text-white" />
|
|
431
|
+
</div>
|
|
432
|
+
<div className="flex-1">
|
|
433
|
+
<div>{t('assistant.actions.generatePodcast')}</div>
|
|
434
|
+
<div className="text-xs text-muted-foreground">
|
|
435
|
+
{t('assistant.actions.generatePodcastDesc')}
|
|
436
|
+
</div>
|
|
437
|
+
</div>
|
|
438
|
+
</button>
|
|
439
|
+
)}
|
|
440
|
+
|
|
441
|
+
{enableSearch && (
|
|
442
|
+
<button
|
|
443
|
+
onClick={() => handleActionSelect('search')}
|
|
444
|
+
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-accent transition-colors text-left group"
|
|
445
|
+
>
|
|
446
|
+
<div className="w-8 h-8 rounded-full bg-[var(--chart-2)]/20 flex items-center justify-center group-hover:bg-[var(--chart-2)] transition-colors">
|
|
447
|
+
<Search className="w-4 h-4 text-[var(--chart-2)] group-hover:text-white" />
|
|
448
|
+
</div>
|
|
449
|
+
<div className="flex-1">
|
|
450
|
+
<div>{t('assistant.actions.search')}</div>
|
|
451
|
+
<div className="text-xs text-muted-foreground">
|
|
452
|
+
{t('assistant.actions.searchDesc')}
|
|
453
|
+
</div>
|
|
454
|
+
</div>
|
|
455
|
+
</button>
|
|
456
|
+
)}
|
|
457
|
+
</div>
|
|
458
|
+
</PopoverContent>
|
|
459
|
+
</Popover>
|
|
460
|
+
)}
|
|
461
|
+
|
|
462
|
+
{enableFileAttachment && (
|
|
463
|
+
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
|
|
464
|
+
<Button
|
|
465
|
+
variant="ghost"
|
|
466
|
+
size="sm"
|
|
467
|
+
onClick={onFileUpload}
|
|
468
|
+
className="h-8 w-8 p-0 rounded-full text-muted-foreground hover:bg-accent hover:text-foreground transition-all duration-200"
|
|
469
|
+
aria-label={t('assistant.attachFile')}
|
|
470
|
+
>
|
|
471
|
+
<Paperclip className="w-4 h-4" />
|
|
472
|
+
</Button>
|
|
473
|
+
</motion.div>
|
|
474
|
+
)}
|
|
475
|
+
</div>
|
|
476
|
+
|
|
477
|
+
{/* Right Actions */}
|
|
478
|
+
<div className="flex items-center gap-1">
|
|
479
|
+
{/* Voice Recording Button */}
|
|
480
|
+
{enableAudioInput && (
|
|
481
|
+
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
|
|
482
|
+
<Button
|
|
483
|
+
variant="ghost"
|
|
484
|
+
size="sm"
|
|
485
|
+
onClick={handleVoiceRecording}
|
|
486
|
+
className={`h-8 w-8 p-0 rounded-full transition-all duration-200 ${
|
|
487
|
+
isRecording
|
|
488
|
+
? 'bg-destructive hover:bg-destructive/90 text-white animate-pulse'
|
|
489
|
+
: 'text-muted-foreground hover:bg-accent hover:text-foreground'
|
|
490
|
+
}`}
|
|
491
|
+
aria-label={
|
|
492
|
+
isRecording ? t('assistant.stopRecording') : t('assistant.suggestWithVoice')
|
|
493
|
+
}
|
|
494
|
+
>
|
|
495
|
+
<Mic className="w-4 h-4" />
|
|
496
|
+
</Button>
|
|
497
|
+
</motion.div>
|
|
498
|
+
)}
|
|
499
|
+
|
|
500
|
+
<AnimatePresence mode="wait">
|
|
501
|
+
<motion.div
|
|
502
|
+
key={hasContent ? 'active' : 'inactive'}
|
|
503
|
+
initial={{ scale: 0.8, opacity: 0 }}
|
|
504
|
+
animate={{ scale: 1, opacity: 1 }}
|
|
505
|
+
exit={{ scale: 0.8, opacity: 0 }}
|
|
506
|
+
transition={{ duration: 0.2, ease: 'easeInOut' }}
|
|
507
|
+
whileHover={hasContent ? { scale: 1.05 } : {}}
|
|
508
|
+
whileTap={hasContent ? { scale: 0.95 } : {}}
|
|
509
|
+
>
|
|
510
|
+
<Button
|
|
511
|
+
onClick={() => {
|
|
512
|
+
onSubmit(selectedAction);
|
|
513
|
+
setSelectedAction(null);
|
|
514
|
+
}}
|
|
515
|
+
size="sm"
|
|
516
|
+
disabled={!hasContent || disabled}
|
|
517
|
+
className={`h-8 w-8 p-0 rounded-full transition-all duration-300 ${
|
|
518
|
+
hasContent && !disabled
|
|
519
|
+
? 'bg-primary hover:bg-primary/90 text-primary-foreground shadow-lg shadow-primary/30 hover:shadow-primary/40'
|
|
520
|
+
: 'bg-muted text-muted-foreground cursor-not-allowed'
|
|
521
|
+
}`}
|
|
522
|
+
aria-label={t('assistant.sendMessage')}
|
|
523
|
+
>
|
|
524
|
+
<motion.div
|
|
525
|
+
animate={{
|
|
526
|
+
rotate: hasContent ? 0 : -45,
|
|
527
|
+
}}
|
|
528
|
+
transition={{ duration: 0.2 }}
|
|
529
|
+
>
|
|
530
|
+
<Send className="w-4 h-4" />
|
|
531
|
+
</motion.div>
|
|
532
|
+
</Button>
|
|
533
|
+
</motion.div>
|
|
534
|
+
</AnimatePresence>
|
|
535
|
+
</div>
|
|
536
|
+
</div>
|
|
537
|
+
</div>
|
|
538
|
+
|
|
539
|
+
{/* Subtle glow effect when focused */}
|
|
540
|
+
<AnimatePresence>
|
|
541
|
+
{isFocused && (
|
|
542
|
+
<motion.div
|
|
543
|
+
className="absolute inset-0 rounded-[var(--radius)] bg-gradient-to-r from-primary/5 via-transparent to-primary/5 pointer-events-none"
|
|
544
|
+
initial={{ opacity: 0 }}
|
|
545
|
+
animate={{ opacity: 1 }}
|
|
546
|
+
exit={{ opacity: 0 }}
|
|
547
|
+
transition={{ duration: 0.3 }}
|
|
548
|
+
/>
|
|
549
|
+
)}
|
|
550
|
+
</AnimatePresence>
|
|
551
|
+
</motion.div>
|
|
552
|
+
|
|
553
|
+
{/* Footer Info */}
|
|
554
|
+
<motion.div
|
|
555
|
+
className="mt-2 text-center"
|
|
556
|
+
initial={{ opacity: 0, y: 5 }}
|
|
557
|
+
animate={{ opacity: 1, y: 0 }}
|
|
558
|
+
transition={{ delay: 0.1 }}
|
|
559
|
+
>
|
|
560
|
+
<p className="text-xs text-muted-foreground">{t('assistant.disclaimer')}</p>
|
|
561
|
+
</motion.div>
|
|
562
|
+
</div>
|
|
563
|
+
);
|
|
564
|
+
}
|