diff --git a/packages/twenty-front/public/images/integrations/integration-mcp-cover-dark.svg b/packages/twenty-front/public/images/ai/ai-mcp-cover-dark.svg
similarity index 100%
rename from packages/twenty-front/public/images/integrations/integration-mcp-cover-dark.svg
rename to packages/twenty-front/public/images/ai/ai-mcp-cover-dark.svg
diff --git a/packages/twenty-front/public/images/integrations/integration-mcp-cover-light.svg b/packages/twenty-front/public/images/ai/ai-mcp-cover-light.svg
similarity index 100%
rename from packages/twenty-front/public/images/integrations/integration-mcp-cover-light.svg
rename to packages/twenty-front/public/images/ai/ai-mcp-cover-light.svg
diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput.tsx
index 5a38d6195d..38e6385fb5 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput.tsx
@@ -8,7 +8,6 @@ import { type VariablePickerComponent } from '@/object-record/record-field/ui/fo
import { InputErrorHelper } from '@/ui/input/components/InputErrorHelper';
import { InputHint } from '@/ui/input/components/InputHint';
import { InputLabel } from '@/ui/input/components/InputLabel';
-import { StyledDropdownButtonContainer } from '@/ui/layout/dropdown/components/StyledDropdownButtonContainer';
import { useFullScreenModal } from '@/ui/layout/fullscreen/hooks/useFullScreenModal';
import { type BreadcrumbProps } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { usePushFocusItemToFocusStack } from '@/ui/utilities/focus/hooks/usePushFocusItemToFocusStack';
@@ -16,10 +15,11 @@ import { useRemoveFocusItemFromFocusStackById } from '@/ui/utilities/focus/hooks
import { FocusComponentType } from '@/ui/utilities/focus/types/FocusComponentType';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
-import { useContext, useId, useState } from 'react';
+import { useId, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconMaximize } from 'twenty-ui/display';
-import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
+import { LightIconButton } from 'twenty-ui/input';
+import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useIsMobile } from 'twenty-ui/utilities';
const StyledAdvancedTextFieldContainerWrapper = styled.div`
@@ -47,8 +47,9 @@ const StyledAdvancedTextFieldInnerContainer = styled.div`
`;
const StyledEditorActionButtonContainer = styled.div`
+ margin-top: ${themeCssVariables.spacing[1]};
position: absolute;
- right: 30px;
+ right: ${themeCssVariables.spacing[1]};
top: ${themeCssVariables.spacing[0]};
z-index: 1;
`;
@@ -63,19 +64,6 @@ const StyledFullScreenEditorContainer = styled.div`
padding: ${themeCssVariables.spacing[2]};
`;
-const StyledFullScreenButtonContainerWrapper = styled.div`
- > * {
- background-color: transparent;
- color: ${themeCssVariables.font.color.tertiary};
- padding: ${themeCssVariables.spacing[2]};
-
- :hover {
- background-color: ${themeCssVariables.background.transparent.light};
- cursor: pointer;
- }
- }
-`;
-
type FormAdvancedTextFieldInputProps = {
label?: string;
error?: string;
@@ -111,7 +99,6 @@ export const FormAdvancedTextFieldInput = ({
maxWidth,
contentType = 'json',
}: FormAdvancedTextFieldInputProps) => {
- const { theme } = useContext(ThemeContext);
const instanceId = useId();
const isMobile = useIsMobile();
const [isFullScreen, setIsFullScreen] = useState(false);
@@ -229,15 +216,12 @@ export const FormAdvancedTextFieldInput = ({
{enableFullScreen && (
{!readonly && !isFullScreen && (
-
-
-
-
-
+
)}
)}
diff --git a/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx b/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx
index cf23c2aa49..75ffd4ef10 100644
--- a/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx
+++ b/packages/twenty-front/src/pages/settings/ai/SettingsAI.tsx
@@ -1,7 +1,3 @@
-import { styled } from '@linaria/react';
-import { Link } from 'react-router-dom';
-
-import { SettingsOptionCardContentButton } from '@/settings/components/SettingsOptions/SettingsOptionCardContentButton';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
import { TabList } from '@/ui/layout/tab-list/components/TabList';
@@ -13,29 +9,19 @@ import { getSettingsPath } from 'twenty-shared/utils';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { t } from '@lingui/core/macro';
import {
- H2Title,
IconChartBar,
IconCpu,
- IconFileText,
IconSettingsBolt,
IconSparkles,
IconTool,
} from 'twenty-ui/display';
-import { Button } from 'twenty-ui/input';
-import { Card, Section } from 'twenty-ui/layout';
-import { SettingsAIMCP } from './components/SettingsAIMCP';
+import { SettingsAIMoreTab } from '~/pages/settings/ai/components/SettingsAIMoreTab';
import { SettingsAIModelsTab } from './components/SettingsAIModelsTab';
import { SettingsAIUsageTab } from './components/SettingsAIUsageTab';
import { SettingsAgentSkills } from './components/SettingsAgentSkills';
import { SettingsToolsTable } from './components/SettingsToolsTable';
import { SETTINGS_AI_TABS } from './constants/SettingsAiTabs';
-const StyledLinkContainer = styled.div`
- > a {
- text-decoration: none;
- }
-`;
-
export const SettingsAI = () => {
const activeTabId = useAtomComponentStateValue(
activeTabIdComponentState,
@@ -104,35 +90,7 @@ export const SettingsAI = () => {
{isSkillsTab && }
{isToolsTab && }
{isUsageTab && }
- {isMoreTab && (
- <>
-
-
-
-
-
-
-
-
- }
- />
-
-
-
- >
- )}
+ {isMoreTab && }
);
diff --git a/packages/twenty-front/src/pages/settings/ai/SettingsAIPrompts.tsx b/packages/twenty-front/src/pages/settings/ai/SettingsAIPrompts.tsx
index 05eaace252..4a9144d270 100644
--- a/packages/twenty-front/src/pages/settings/ai/SettingsAIPrompts.tsx
+++ b/packages/twenty-front/src/pages/settings/ai/SettingsAIPrompts.tsx
@@ -1,28 +1,19 @@
-import { CombinedGraphQLErrors } from '@apollo/client/errors';
import { styled } from '@linaria/react';
-import { useDebouncedCallback } from 'use-debounce';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
-import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { FormAdvancedTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
-import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
-import { t } from '@lingui/core/macro';
-import { useState } from 'react';
-import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
+import { useQuery } from '@apollo/client/react';
+import { t } from '@lingui/core/macro';
import { SettingsPath } from 'twenty-shared/types';
import { getSettingsPath, isDefined } from 'twenty-shared/utils';
-import { formatNumber } from '~/utils/format/formatNumber';
import { H2Title } from 'twenty-ui/display';
import { Section } from 'twenty-ui/layout';
import { themeCssVariables } from 'twenty-ui/theme-constants';
-import { useQuery, useMutation } from '@apollo/client/react';
-import {
- GetAiSystemPromptPreviewDocument,
- UpdateWorkspaceDocument,
-} from '~/generated-metadata/graphql';
+import { GetAiSystemPromptPreviewDocument } from '~/generated-metadata/graphql';
+import { formatNumber } from '~/utils/format/formatNumber';
const StyledFormContainer = styled.div`
display: flex;
@@ -41,83 +32,12 @@ const StyledTokenBadge = styled.span`
`;
export const SettingsAIPrompts = () => {
- const { enqueueErrorSnackBar } = useSnackBar();
- const [currentWorkspace, setCurrentWorkspace] = useAtomState(
- currentWorkspaceState,
- );
const currentWorkspaceMember = useAtomStateValue(currentWorkspaceMemberState);
- const [updateWorkspace] = useMutation(UpdateWorkspaceDocument);
const { data: previewData, loading: previewLoading } = useQuery(
GetAiSystemPromptPreviewDocument,
);
- const [workspaceInstructions, setWorkspaceInstructions] = useState(
- currentWorkspace?.aiAdditionalInstructions ?? '',
- );
- const [originalInstructions, setOriginalInstructions] = useState(
- currentWorkspace?.aiAdditionalInstructions ?? '',
- );
-
- const handleWorkspaceInstructionsInit = () => {
- if (currentWorkspace?.aiAdditionalInstructions !== undefined) {
- setWorkspaceInstructions(currentWorkspace.aiAdditionalInstructions ?? '');
- setOriginalInstructions(currentWorkspace.aiAdditionalInstructions ?? '');
- }
- };
-
- if (
- currentWorkspace?.aiAdditionalInstructions !== undefined &&
- originalInstructions === '' &&
- currentWorkspace.aiAdditionalInstructions !== null &&
- currentWorkspace.aiAdditionalInstructions !== originalInstructions
- ) {
- handleWorkspaceInstructionsInit();
- }
-
- const autoSave = useDebouncedCallback(async (newValue: string) => {
- if (!currentWorkspace?.id || newValue === originalInstructions) {
- return;
- }
-
- try {
- setCurrentWorkspace({
- ...currentWorkspace,
- aiAdditionalInstructions: newValue || null,
- });
-
- await updateWorkspace({
- variables: {
- input: {
- aiAdditionalInstructions: newValue || null,
- },
- },
- });
-
- setOriginalInstructions(newValue);
- } catch (error) {
- setCurrentWorkspace({
- ...currentWorkspace,
- aiAdditionalInstructions: originalInstructions || null,
- });
-
- if (CombinedGraphQLErrors.is(error)) {
- enqueueErrorSnackBar({
- apolloError: error,
- });
- } else {
- enqueueErrorSnackBar({
- message: t`Failed to save workspace instructions`,
- });
- }
- }
- }, 1000);
-
- const handleWorkspaceInstructionsChange = (value: string) => {
- setWorkspaceInstructions(value);
- autoSave(value);
- };
-
const preview = previewData?.getAISystemPromptPreview;
const sections = preview?.sections ?? [];
@@ -210,36 +130,6 @@ export const SettingsAIPrompts = () => {
))}
-
-
{
const { t } = useLingui();
const { copyToClipboard } = useCopyToClipboard();
const [authMethod, setAuthMethod] = useState('oauth');
+ const { colorScheme } = useContext(ThemeContext);
+ const coverImage =
+ colorScheme === 'light'
+ ? '/images/ai/ai-mcp-cover-light.svg'
+ : '/images/ai/ai-mcp-cover-dark.svg';
const oauthConfig = JSON.stringify(
{
@@ -87,6 +103,7 @@ export const SettingsAIMCP = () => {
folding: false,
selectionHighlight: false,
occurrencesHighlight: 'off' as const,
+ scrollBeyondLastLine: false,
hover: {
enabled: false,
},
@@ -104,30 +121,30 @@ export const SettingsAIMCP = () => {
-
-
-
+
+
-
-
-
+
);
diff --git a/packages/twenty-front/src/pages/settings/ai/components/SettingsAIMoreTab.tsx b/packages/twenty-front/src/pages/settings/ai/components/SettingsAIMoreTab.tsx
new file mode 100644
index 0000000000..52e36701cf
--- /dev/null
+++ b/packages/twenty-front/src/pages/settings/ai/components/SettingsAIMoreTab.tsx
@@ -0,0 +1,148 @@
+import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
+import { FormAdvancedTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput';
+import { SettingsCard } from '@/settings/components/SettingsCard';
+import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
+import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState';
+import { CombinedGraphQLErrors } from '@apollo/client';
+import { useMutation, useQuery } from '@apollo/client/react';
+import { styled } from '@linaria/react';
+import { t } from '@lingui/core/macro';
+import { useContext, useState } from 'react';
+import { SettingsPath } from 'twenty-shared/types';
+import { getSettingsPath, isDefined } from 'twenty-shared/utils';
+import { H2Title, IconPrompt } from 'twenty-ui/display';
+import { Section } from 'twenty-ui/layout';
+import { UndecoratedLink } from 'twenty-ui/navigation';
+import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
+import { useDebouncedCallback } from 'use-debounce';
+import {
+ GetAiSystemPromptPreviewDocument,
+ UpdateWorkspaceDocument,
+} from '~/generated-metadata/graphql';
+import { SettingsAIMCP } from '~/pages/settings/ai/components/SettingsAIMCP';
+import { formatNumber } from '~/utils/format/formatNumber';
+
+const StyledFormContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: ${themeCssVariables.spacing[4]};
+`;
+
+export const SettingsAIMoreTab = () => {
+ const { theme } = useContext(ThemeContext);
+ const { enqueueErrorSnackBar } = useSnackBar();
+ const [currentWorkspace, setCurrentWorkspace] = useAtomState(
+ currentWorkspaceState,
+ );
+ const [updateWorkspace] = useMutation(UpdateWorkspaceDocument);
+ const { data: previewData } = useQuery(GetAiSystemPromptPreviewDocument);
+
+ const initialInstructions = currentWorkspace?.aiAdditionalInstructions ?? '';
+ const [workspaceInstructions, setWorkspaceInstructions] =
+ useState(initialInstructions);
+ const [originalInstructions, setOriginalInstructions] =
+ useState(initialInstructions);
+
+ const autoSave = useDebouncedCallback(async (newValue: string) => {
+ if (!currentWorkspace?.id || newValue === originalInstructions) {
+ return;
+ }
+
+ try {
+ setCurrentWorkspace({
+ ...currentWorkspace,
+ aiAdditionalInstructions: newValue || null,
+ });
+
+ await updateWorkspace({
+ variables: {
+ input: {
+ aiAdditionalInstructions: newValue || null,
+ },
+ },
+ });
+
+ setOriginalInstructions(newValue);
+ } catch (error) {
+ setCurrentWorkspace({
+ ...currentWorkspace,
+ aiAdditionalInstructions: originalInstructions || null,
+ });
+
+ if (CombinedGraphQLErrors.is(error)) {
+ enqueueErrorSnackBar({
+ apolloError: error,
+ });
+ } else {
+ enqueueErrorSnackBar({
+ message: t`Failed to save workspace instructions`,
+ });
+ }
+ }
+ }, 1000);
+
+ const handleWorkspaceInstructionsChange = (value: string) => {
+ setWorkspaceInstructions(value);
+ autoSave(value);
+ };
+
+ const systemPromptTokenCount =
+ previewData?.getAISystemPromptPreview.estimatedTokenCount;
+ const systemPromptDescription = isDefined(systemPromptTokenCount)
+ ? t`Read the system prompts to understand how the AI works (~${formatNumber(
+ systemPromptTokenCount,
+ {
+ abbreviate: true,
+ decimals: 1,
+ },
+ )} tokens)`
+ : t`Read the system prompts to understand how the AI works`;
+
+ return (
+ <>
+
+
+
+
+
+
+
+ }
+ title={t`Read system prompts`}
+ />
+
+
+ >
+ );
+};
diff --git a/packages/twenty-front/src/pages/settings/ai/components/SettingsAgentSkills.tsx b/packages/twenty-front/src/pages/settings/ai/components/SettingsAgentSkills.tsx
index c913f5ed84..0ecb6df1c8 100644
--- a/packages/twenty-front/src/pages/settings/ai/components/SettingsAgentSkills.tsx
+++ b/packages/twenty-front/src/pages/settings/ai/components/SettingsAgentSkills.tsx
@@ -25,6 +25,7 @@ import { normalizeSearchText } from '~/utils/normalizeSearchText';
import { SettingsAgentSkillsTable } from './SettingsAgentSkillsTable';
const StyledCoverImage = styled.div`
+ background-position: center;
background-size: cover;
height: 160px;
overflow: hidden;
diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts
index 78a3801326..122f50f7da 100644
--- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts
+++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts
@@ -8,8 +8,8 @@ export {
IconAlignLeft,
IconAlignRight,
IconApi,
- IconAppWindow,
IconApps,
+ IconAppWindow,
IconArchive,
IconArchiveOff,
IconArrowBackUp,
@@ -17,12 +17,12 @@ export {
IconArrowLeft,
IconArrowMerge,
IconArrowRight,
- IconArrowUp,
- IconArrowUpRight,
IconArrowsDiagonal,
IconArrowsSort,
IconArrowsSplit2,
IconArrowsVertical,
+ IconArrowUp,
+ IconArrowUpRight,
IconAt,
IconAxisX,
IconAxisY,
@@ -47,8 +47,8 @@ export {
IconBrandGoogle,
IconBrandGraphql,
IconBrandLinkedin,
- IconBrandOpenai,
IconBrandNpm,
+ IconBrandOpenai,
IconBrandX,
IconBriefcase,
IconBroadcast,
@@ -77,8 +77,8 @@ export {
IconChevronLeft,
IconChevronRight,
IconChevronRightPipe,
- IconChevronUp,
IconChevronsRight,
+ IconChevronUp,
IconCircle,
IconCircleDashed,
IconCircleDot,
@@ -183,8 +183,8 @@ export {
IconFilterCog,
IconFilterOff,
IconFilterPlus,
- IconFilterX,
IconFilters,
+ IconFilterX,
IconFlag,
IconFlask,
IconFocusCentered,
@@ -310,6 +310,7 @@ export {
IconPresentation,
IconPrinter,
IconProgressCheck,
+ IconPrompt,
IconPuzzle,
IconPuzzle2,
IconQuestionMark,
@@ -345,10 +346,10 @@ export {
IconShield,
IconSitemap,
IconSlash,
- IconSortAZ,
IconSortAscending,
IconSortAscendingLetters,
IconSortAscendingNumbers,
+ IconSortAZ,
IconSortDescending,
IconSortDescendingLetters,
IconSortDescendingNumbers,
diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts
index f44130b551..a6cc282de1 100644
--- a/packages/twenty-ui/src/display/index.ts
+++ b/packages/twenty-ui/src/display/index.ts
@@ -86,8 +86,8 @@ export {
IconAlignLeft,
IconAlignRight,
IconApi,
- IconAppWindow,
IconApps,
+ IconAppWindow,
IconArchive,
IconArchiveOff,
IconArrowBackUp,
@@ -95,12 +95,12 @@ export {
IconArrowLeft,
IconArrowMerge,
IconArrowRight,
- IconArrowUp,
- IconArrowUpRight,
IconArrowsDiagonal,
IconArrowsSort,
IconArrowsSplit2,
IconArrowsVertical,
+ IconArrowUp,
+ IconArrowUpRight,
IconAt,
IconAxisX,
IconAxisY,
@@ -125,8 +125,8 @@ export {
IconBrandGoogle,
IconBrandGraphql,
IconBrandLinkedin,
- IconBrandOpenai,
IconBrandNpm,
+ IconBrandOpenai,
IconBrandX,
IconBriefcase,
IconBroadcast,
@@ -155,8 +155,8 @@ export {
IconChevronLeft,
IconChevronRight,
IconChevronRightPipe,
- IconChevronUp,
IconChevronsRight,
+ IconChevronUp,
IconCircle,
IconCircleDashed,
IconCircleDot,
@@ -261,8 +261,8 @@ export {
IconFilterCog,
IconFilterOff,
IconFilterPlus,
- IconFilterX,
IconFilters,
+ IconFilterX,
IconFlag,
IconFlask,
IconFocusCentered,
@@ -388,6 +388,7 @@ export {
IconPresentation,
IconPrinter,
IconProgressCheck,
+ IconPrompt,
IconPuzzle,
IconPuzzle2,
IconQuestionMark,
@@ -423,10 +424,10 @@ export {
IconShield,
IconSitemap,
IconSlash,
- IconSortAZ,
IconSortAscending,
IconSortAscendingLetters,
IconSortAscendingNumbers,
+ IconSortAZ,
IconSortDescending,
IconSortDescendingLetters,
IconSortDescendingNumbers,