From 4cdb7d61b2ef2e882c2f75b4fce860f836017517 Mon Sep 17 00:00:00 2001 From: "Abdullah." <125115953+mabdullahabaid@users.noreply.github.com> Date: Mon, 9 Feb 2026 19:29:32 +0500 Subject: [PATCH] Redesign AI chat and add pre-existing prompts. (#17787) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Redesigned AI-chat based on the following provided design.

--------- Co-authored-by: Félix Malfait --- .../ai/components/AIChatEmptyState.tsx | 43 +--- .../src/modules/ai/components/AIChatTab.tsx | 116 ++++++---- .../internal/AIChatContextUsageButton.tsx | 9 +- .../internal/AgentChatFileUploadButton.tsx | 12 +- .../internal/ContextUsageProgressRing.tsx | 6 +- .../components/internal/SendMessageButton.tsx | 12 +- .../AIChatSuggestedPrompts.tsx | 58 +++++ .../default-suggested-prompts.ts | 48 ++++ .../CommandMenuTopBarRightCornerIcon.tsx | 24 +- .../utils/parse-clickhouse-filter.util.ts | 209 ++++++++++++++++++ .../utils/parse-clickhouse-order-by.util.ts | 28 +++ .../validate-allowed-value.util.spec.ts | 30 +++ .../utils/validate-allowed-value.util.ts | 12 + 13 files changed, 494 insertions(+), 113 deletions(-) create mode 100644 packages/twenty-front/src/modules/ai/components/suggested-prompts/AIChatSuggestedPrompts.tsx create mode 100644 packages/twenty-front/src/modules/ai/components/suggested-prompts/default-suggested-prompts.ts create mode 100644 packages/twenty-server/src/engine/api/clickhouse-query-runners/utils/parse-clickhouse-filter.util.ts create mode 100644 packages/twenty-server/src/engine/api/clickhouse-query-runners/utils/parse-clickhouse-order-by.util.ts create mode 100644 packages/twenty-server/src/engine/core-modules/sql-sanitization/utils/__tests__/validate-allowed-value.util.spec.ts create mode 100644 packages/twenty-server/src/engine/core-modules/sql-sanitization/utils/validate-allowed-value.util.ts diff --git a/packages/twenty-front/src/modules/ai/components/AIChatEmptyState.tsx b/packages/twenty-front/src/modules/ai/components/AIChatEmptyState.tsx index db1d63e7e7..fc83b72db1 100644 --- a/packages/twenty-front/src/modules/ai/components/AIChatEmptyState.tsx +++ b/packages/twenty-front/src/modules/ai/components/AIChatEmptyState.tsx @@ -1,52 +1,19 @@ -import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { t } from '@lingui/core/macro'; -import { IconSparkles } from 'twenty-ui/display'; + +import { AIChatSuggestedPrompts } from '@/ai/components/suggested-prompts/AIChatSuggestedPrompts'; + const StyledEmptyState = styled.div` display: flex; flex-direction: column; flex: 1; - gap: ${({ theme }) => theme.spacing(2)}; - align-items: center; - justify-content: center; + justify-content: flex-end; height: 100%; `; -const StyledSparkleIcon = styled.div` - align-items: center; - background: ${({ theme }) => theme.background.transparent.blue}; - border-radius: ${({ theme }) => theme.border.radius.sm}; - padding: ${({ theme }) => theme.spacing(2.5)}; - display: flex; - justify-content: center; - margin-bottom: ${({ theme }) => theme.spacing(2)}; -`; - -const StyledTitle = styled.div` - font-size: ${({ theme }) => theme.font.size.lg}; - color: ${({ theme }) => theme.font.color.primary}; - font-weight: 600; -`; - -const StyledDescription = styled.div` - color: ${({ theme }) => theme.font.color.secondary}; - text-align: center; - max-width: 85%; - font-size: ${({ theme }) => theme.font.size.md}; -`; - export const AIChatEmptyState = () => { - const theme = useTheme(); - return ( - - - - {t`Chat`} - - {t`Start a conversation with your AI agent to get workflow insights, task assistance, and process guidance`} - + ); }; diff --git a/packages/twenty-front/src/modules/ai/components/AIChatTab.tsx b/packages/twenty-front/src/modules/ai/components/AIChatTab.tsx index e5294d2bc3..64d64c9bef 100644 --- a/packages/twenty-front/src/modules/ai/components/AIChatTab.tsx +++ b/packages/twenty-front/src/modules/ai/components/AIChatTab.tsx @@ -1,15 +1,14 @@ import { TextArea } from '@/ui/input/components/TextArea'; import styled from '@emotion/styled'; -import { IconHistory, IconMessageCirclePlus } from 'twenty-ui/display'; +import { IconHistory } from 'twenty-ui/display'; +import { IconButton } from 'twenty-ui/input'; import { DropZone } from '@/activities/files/components/DropZone'; import { AgentChatFileUploadButton } from '@/ai/components/internal/AgentChatFileUploadButton'; -import { useCreateNewAIChatThread } from '@/ai/hooks/useCreateNewAIChatThread'; import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; -import { AgentMessageRole } from '@/ai/constants/AgentMessageRole'; import { AIChatEmptyState } from '@/ai/components/AIChatEmptyState'; import { AIChatMessage } from '@/ai/components/AIChatMessage'; import { AIChatStandaloneError } from '@/ai/components/AIChatStandaloneError'; @@ -17,6 +16,7 @@ import { AIChatContextUsageButton } from '@/ai/components/internal/AIChatContext import { AIChatSkeletonLoader } from '@/ai/components/internal/AIChatSkeletonLoader'; import { AgentChatContextPreview } from '@/ai/components/internal/AgentChatContextPreview'; import { SendMessageButton } from '@/ai/components/internal/SendMessageButton'; +import { AgentMessageRole } from '@/ai/constants/AgentMessageRole'; import { AI_CHAT_INPUT_ID } from '@/ai/constants/AiChatInputId'; import { AI_CHAT_SCROLL_WRAPPER_ID } from '@/ai/constants/AiChatScrollWrapperId'; import { useAIChatFileUpload } from '@/ai/hooks/useAIChatFileUpload'; @@ -25,7 +25,6 @@ import { agentChatInputState } from '@/ai/states/agentChatInputState'; import { t } from '@lingui/core/macro'; import { useState } from 'react'; import { useRecoilState } from 'recoil'; -import { Button } from 'twenty-ui/input'; const StyledContainer = styled.div<{ isDraggingFile: boolean }>` background: ${({ theme }) => theme.background.primary}; @@ -46,6 +45,46 @@ const StyledInputArea = styled.div` background: ${({ theme }) => theme.background.primary}; `; +const StyledInputBox = styled.div` + background-color: ${({ theme }) => theme.background.transparent.lighter}; + border: 1px solid ${({ theme }) => theme.border.color.medium}; + border-radius: ${({ theme }) => theme.border.radius.sm}; + display: flex; + flex-direction: column; + gap: ${({ theme }) => theme.spacing(2)}; + min-height: 140px; + padding: ${({ theme }) => theme.spacing(2)}; + width: 100%; + box-sizing: border-box; + + &:focus-within { + border-color: ${({ theme }) => theme.color.blue}; + box-shadow: 0px 0px 0px 3px ${({ theme }) => theme.color.transparent.blue2}; + } +`; + +const StyledTextAreaWrapper = styled.div` + display: flex; + flex: 1; + flex-direction: column; + min-height: 0; +`; + +const StyledChatTextArea = styled(TextArea)` + && { + background: transparent; + border: none; + border-radius: 0; + box-shadow: none; + padding: 0; + } + + &&:focus { + border: none; + box-shadow: none; + } +`; + const StyledScrollWrapper = styled(ScrollWrapper)` display: flex; flex: 1; @@ -59,7 +98,8 @@ const StyledScrollWrapper = styled(ScrollWrapper)` const StyledButtonsContainer = styled.div` display: flex; flex-direction: row; - gap: ${({ theme }) => theme.spacing(2)}; + gap: ${({ theme }) => theme.spacing(1)}; + justify-content: flex-end; `; export const AIChatTab = () => { @@ -72,7 +112,6 @@ export const AIChatTab = () => { useRecoilState(agentChatInputState); const { uploadFiles } = useAIChatFileUpload(); - const { createChatThread } = useCreateNewAIChatThread(); const { navigateCommandMenu } = useCommandMenu(); return ( @@ -115,7 +154,9 @@ export const AIChatTab = () => { )} )} - {messages.length === 0 && !error && } + {messages.length === 0 && !error && !isLoading && ( + + )} {messages.length === 0 && error && !isLoading && ( )} @@ -123,37 +164,36 @@ export const AIChatTab = () => { -