feat: design the ai-models tab on settings ai page (#18147)
Remove AI model selectors from the settings tab on AI settings page, rename that tab to "More" and create a separate "Models" tab as per the design. https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=91311-278313&m=dev --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@@ -13,15 +13,16 @@ import { getSettingsPath } from 'twenty-shared/utils';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import {
|
||||
H2Title,
|
||||
IconCpu,
|
||||
IconFileText,
|
||||
IconSettings,
|
||||
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 { SettingsAIRouterSettings } from './components/SettingsAIRouterSettings';
|
||||
import { SettingsAIModelsTab } from './components/SettingsAIModelsTab';
|
||||
import { SettingsSkillsTable } from './components/SettingsSkillsTable';
|
||||
import { SettingsToolsTable } from './components/SettingsToolsTable';
|
||||
import { SETTINGS_AI_TABS } from './constants/SettingsAiTabs';
|
||||
@@ -37,6 +38,11 @@ export const SettingsAI = () => {
|
||||
);
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: SETTINGS_AI_TABS.TABS_IDS.MODELS,
|
||||
title: t`Models`,
|
||||
Icon: IconCpu,
|
||||
},
|
||||
{
|
||||
id: SETTINGS_AI_TABS.TABS_IDS.SKILLS,
|
||||
title: t`Skills`,
|
||||
@@ -48,15 +54,16 @@ export const SettingsAI = () => {
|
||||
Icon: IconTool,
|
||||
},
|
||||
{
|
||||
id: SETTINGS_AI_TABS.TABS_IDS.SETTINGS,
|
||||
title: t`Settings`,
|
||||
Icon: IconSettings,
|
||||
id: SETTINGS_AI_TABS.TABS_IDS.MORE,
|
||||
title: t`More`,
|
||||
Icon: IconSettingsBolt,
|
||||
},
|
||||
];
|
||||
|
||||
const isModelsTab = activeTabId === SETTINGS_AI_TABS.TABS_IDS.MODELS;
|
||||
const isSkillsTab = activeTabId === SETTINGS_AI_TABS.TABS_IDS.SKILLS;
|
||||
const isToolsTab = activeTabId === SETTINGS_AI_TABS.TABS_IDS.TOOLS;
|
||||
const isSettingsTab = activeTabId === SETTINGS_AI_TABS.TABS_IDS.SETTINGS;
|
||||
const isMoreTab = activeTabId === SETTINGS_AI_TABS.TABS_IDS.MORE;
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
@@ -74,11 +81,11 @@ export const SettingsAI = () => {
|
||||
tabs={tabs}
|
||||
componentInstanceId={SETTINGS_AI_TABS.COMPONENT_INSTANCE_ID}
|
||||
/>
|
||||
{isModelsTab && <SettingsAIModelsTab />}
|
||||
{isSkillsTab && <SettingsSkillsTable />}
|
||||
{isToolsTab && <SettingsToolsTable />}
|
||||
{isSettingsTab && (
|
||||
{isMoreTab && (
|
||||
<>
|
||||
<SettingsAIRouterSettings />
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`System Prompt`}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { DEFAULT_FAST_MODEL } from '@/ai/constants/DefaultFastModel';
|
||||
import { DEFAULT_SMART_MODEL } from '@/ai/constants/DefaultSmartModel';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { aiModelsState } from '@/client-config/states/aiModelsState';
|
||||
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { H2Title, IconBolt } from 'twenty-ui/display';
|
||||
import { Card, Section } from 'twenty-ui/layout';
|
||||
import { useUpdateWorkspaceMutation } from '~/generated-metadata/graphql';
|
||||
import { PROVIDER_CONFIG } from '~/pages/settings/ai/constants/SettingsAiModelProviders';
|
||||
|
||||
const VIRTUAL_MODEL_IDS: Set<string> = new Set([
|
||||
DEFAULT_SMART_MODEL,
|
||||
DEFAULT_FAST_MODEL,
|
||||
]);
|
||||
|
||||
export const SettingsAIModelsTab = () => {
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||
currentWorkspaceState,
|
||||
);
|
||||
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||
|
||||
const aiModels = useRecoilValueV2(aiModelsState);
|
||||
|
||||
const realModels = aiModels.filter(
|
||||
(model) => !VIRTUAL_MODEL_IDS.has(model.modelId),
|
||||
);
|
||||
|
||||
const currentSmartModel = currentWorkspace?.smartModel;
|
||||
|
||||
const defaultModelOptions = realModels
|
||||
.filter((model) => !model.deprecated || model.modelId === currentSmartModel)
|
||||
.map((model) => ({
|
||||
value: model.modelId,
|
||||
label: model.label,
|
||||
Icon: PROVIDER_CONFIG[model.provider.toUpperCase()]?.Icon,
|
||||
}));
|
||||
|
||||
const handleDefaultModelChange = async (value: string) => {
|
||||
if (!currentWorkspace?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previousSmartModel = currentWorkspace.smartModel;
|
||||
|
||||
try {
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
smartModel: value,
|
||||
});
|
||||
|
||||
await updateWorkspace({
|
||||
variables: {
|
||||
input: {
|
||||
smartModel: value,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Default model updated successfully`,
|
||||
});
|
||||
} catch {
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
smartModel: previousSmartModel,
|
||||
});
|
||||
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Failed to update default model`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`Default`}
|
||||
description={t`Configure your default AI model`}
|
||||
/>
|
||||
|
||||
<Card rounded>
|
||||
<SettingsOptionCardContentSelect
|
||||
Icon={IconBolt}
|
||||
title={t`Default Model`}
|
||||
description={t`Default model for new chats and agents`}
|
||||
>
|
||||
<Select
|
||||
dropdownId="default-model-select"
|
||||
value={currentSmartModel}
|
||||
onChange={handleDefaultModelChange}
|
||||
options={defaultModelOptions}
|
||||
selectSizeVariant="small"
|
||||
/>
|
||||
</SettingsOptionCardContentSelect>
|
||||
</Card>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -1,220 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { DEFAULT_FAST_MODEL } from '@/ai/constants/DefaultFastModel';
|
||||
import { DEFAULT_SMART_MODEL } from '@/ai/constants/DefaultSmartModel';
|
||||
import {
|
||||
useAiModelLabel,
|
||||
useAiModelOptions,
|
||||
} from '@/ai/hooks/useAiModelOptions';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { aiModelsState } from '@/client-config/states/aiModelsState';
|
||||
import {
|
||||
StyledSettingsCardContent,
|
||||
StyledSettingsCardDescription,
|
||||
StyledSettingsCardIcon,
|
||||
StyledSettingsCardTitle,
|
||||
} from '@/settings/components/SettingsOptions/SettingsCardContentBase';
|
||||
import { SettingsOptionIconCustomizer } from '@/settings/components/SettingsOptions/SettingsOptionIconCustomizer';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { H2Title, IconBolt, IconBrain } from 'twenty-ui/display';
|
||||
import { Card, Section } from 'twenty-ui/layout';
|
||||
import { useUpdateWorkspaceMutation } from '~/generated-metadata/graphql';
|
||||
import { useRecoilValueV2 } from '@/ui/utilities/state/jotai/hooks/useRecoilValueV2';
|
||||
|
||||
const StyledSelectContainer = styled.div`
|
||||
justify-content: flex-end;
|
||||
margin-left: auto;
|
||||
max-width: 120px;
|
||||
`;
|
||||
|
||||
const StyledErrorMessage = styled.div`
|
||||
color: ${({ theme }) => theme.color.red};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
margin-top: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const SettingsAIRouterSettings = () => {
|
||||
const { enqueueSuccessSnackBar, enqueueErrorSnackBar } = useSnackBar();
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||
currentWorkspaceState,
|
||||
);
|
||||
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||
|
||||
const aiModels = useRecoilValueV2(aiModelsState);
|
||||
const activeModelOptions = useAiModelOptions();
|
||||
const fastModelLabel = useAiModelLabel(currentWorkspace?.fastModel);
|
||||
const smartModelLabel = useAiModelLabel(currentWorkspace?.smartModel);
|
||||
|
||||
const currentFastModel = aiModels.find(
|
||||
(m) => m.modelId === currentWorkspace?.fastModel,
|
||||
);
|
||||
const currentSmartModel = aiModels.find(
|
||||
(m) => m.modelId === currentWorkspace?.smartModel,
|
||||
);
|
||||
|
||||
const fastModelOptions =
|
||||
currentFastModel?.deprecated === true
|
||||
? [
|
||||
{
|
||||
value: currentWorkspace?.fastModel ?? '',
|
||||
label: `${fastModelLabel} (deprecated)`,
|
||||
},
|
||||
...activeModelOptions,
|
||||
]
|
||||
: activeModelOptions;
|
||||
|
||||
const smartModelOptions =
|
||||
currentSmartModel?.deprecated === true
|
||||
? [
|
||||
{
|
||||
value: currentWorkspace?.smartModel ?? '',
|
||||
label: `${smartModelLabel} (deprecated)`,
|
||||
},
|
||||
...activeModelOptions,
|
||||
]
|
||||
: activeModelOptions;
|
||||
|
||||
const noModelsAvailable = activeModelOptions.length === 0;
|
||||
|
||||
const handleFastModelChange = async (value: string) => {
|
||||
if (!currentWorkspace?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newValue = value;
|
||||
const previousValue = currentWorkspace?.fastModel || DEFAULT_FAST_MODEL;
|
||||
|
||||
try {
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
fastModel: newValue,
|
||||
});
|
||||
|
||||
await updateWorkspace({
|
||||
variables: {
|
||||
input: {
|
||||
fastModel: newValue,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Fast model updated successfully`,
|
||||
});
|
||||
} catch {
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
fastModel: previousValue,
|
||||
});
|
||||
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Failed to update fast model`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSmartModelChange = async (value: string) => {
|
||||
if (!currentWorkspace?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newValue = value;
|
||||
const previousValue = currentWorkspace?.smartModel || DEFAULT_SMART_MODEL;
|
||||
|
||||
try {
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
smartModel: newValue,
|
||||
});
|
||||
|
||||
await updateWorkspace({
|
||||
variables: {
|
||||
input: {
|
||||
smartModel: newValue,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
enqueueSuccessSnackBar({
|
||||
message: t`Smart model updated successfully`,
|
||||
});
|
||||
} catch {
|
||||
setCurrentWorkspace({
|
||||
...currentWorkspace,
|
||||
smartModel: previousValue,
|
||||
});
|
||||
|
||||
enqueueErrorSnackBar({
|
||||
message: t`Failed to update smart model`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`AI Models`}
|
||||
description={t`Configure AI models for routing and planning`}
|
||||
/>
|
||||
|
||||
{noModelsAvailable ? (
|
||||
<Card rounded>
|
||||
<StyledSettingsCardContent>
|
||||
<StyledErrorMessage>
|
||||
{t`No models available. Please configure AI models in your workspace settings.`}
|
||||
</StyledErrorMessage>
|
||||
</StyledSettingsCardContent>
|
||||
</Card>
|
||||
) : (
|
||||
<Card rounded>
|
||||
<StyledSettingsCardContent>
|
||||
<StyledSettingsCardIcon>
|
||||
<SettingsOptionIconCustomizer Icon={IconBolt} />
|
||||
</StyledSettingsCardIcon>
|
||||
<div>
|
||||
<StyledSettingsCardTitle>{t`Fast Model`}</StyledSettingsCardTitle>
|
||||
<StyledSettingsCardDescription>
|
||||
{t`Quick model for routing decisions`}
|
||||
</StyledSettingsCardDescription>
|
||||
</div>
|
||||
<StyledSelectContainer>
|
||||
<Select
|
||||
dropdownId="fast-model-select"
|
||||
value={currentWorkspace?.fastModel || DEFAULT_FAST_MODEL}
|
||||
onChange={handleFastModelChange}
|
||||
options={fastModelOptions}
|
||||
selectSizeVariant="small"
|
||||
/>
|
||||
</StyledSelectContainer>
|
||||
</StyledSettingsCardContent>
|
||||
|
||||
<StyledSettingsCardContent>
|
||||
<StyledSettingsCardIcon>
|
||||
<SettingsOptionIconCustomizer Icon={IconBrain} />
|
||||
</StyledSettingsCardIcon>
|
||||
<div>
|
||||
<StyledSettingsCardTitle>
|
||||
{t`Smart Model`}
|
||||
</StyledSettingsCardTitle>
|
||||
<StyledSettingsCardDescription>
|
||||
{t`Advanced model for complex planning`}
|
||||
</StyledSettingsCardDescription>
|
||||
</div>
|
||||
<StyledSelectContainer>
|
||||
<Select
|
||||
dropdownId="smart-model-select"
|
||||
value={currentWorkspace?.smartModel || DEFAULT_SMART_MODEL}
|
||||
onChange={handleSmartModelChange}
|
||||
options={smartModelOptions}
|
||||
selectSizeVariant="small"
|
||||
/>
|
||||
</StyledSelectContainer>
|
||||
</StyledSettingsCardContent>
|
||||
</Card>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
IconBrandAnthropic,
|
||||
IconBrandGoogle,
|
||||
IconBrandGroq,
|
||||
IconBrandMistral,
|
||||
IconBrandOpenai,
|
||||
IconBrandXai,
|
||||
IconRobot,
|
||||
type IconComponent,
|
||||
} from 'twenty-ui/display';
|
||||
|
||||
export const PROVIDER_CONFIG: Record<
|
||||
string,
|
||||
{ label: string; Icon: IconComponent }
|
||||
> = {
|
||||
OPENAI: { label: 'OpenAI', Icon: IconBrandOpenai },
|
||||
ANTHROPIC: { label: 'Anthropic', Icon: IconBrandAnthropic },
|
||||
XAI: { label: 'xAI', Icon: IconBrandXai },
|
||||
GOOGLE: { label: 'Google', Icon: IconBrandGoogle },
|
||||
GROQ: { label: 'Groq', Icon: IconBrandGroq },
|
||||
MISTRAL: { label: 'Mistral', Icon: IconBrandMistral },
|
||||
NONE: { label: '', Icon: IconRobot },
|
||||
};
|
||||
@@ -1,8 +1,9 @@
|
||||
export const SETTINGS_AI_TABS = {
|
||||
COMPONENT_INSTANCE_ID: 'settings-ai-tab-list',
|
||||
TABS_IDS: {
|
||||
MODELS: 'models',
|
||||
SKILLS: 'skills',
|
||||
TOOLS: 'tools',
|
||||
SETTINGS: 'settings',
|
||||
MORE: 'more',
|
||||
},
|
||||
} as const;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 1200 1200">
|
||||
<path fill="currentColor" d="M233.96 800.21 468.64 668.54l3.95-11.44-3.95-6.36h-11.44l-39.22-2.42-134.09-3.62-116.3-4.83-112.67-6.04L26.58 627.79 0 592.75l2.74-17.48 23.84-16.03 34.15 2.98 75.46 5.15 113.24 7.81 82.15 4.83 121.69 12.65 19.33-.001 2.74-7.81-6.6-4.83-5.16-4.83L346.39 495.79 219.54 411.87l-66.44-48.32-35.92-24.48-18.12-22.96-7.81-50.09 32.62-35.92 43.81 2.98 11.2 2.98 44.37 34.15 94.79 73.37 123.79 91.17 18.12 15.06 7.25-5.15.89-3.63-8.14-13.61-67.46-121.69L320.78 181.93l-31.97-51.3-8.46-30.77c-2.98-12.64-5.15-23.27-5.15-36.24l37.13-50.42 20.54-6.6 49.53 6.6 20.86 18.12 30.77 70.39 49.85 110.82L561.18 363.22l22.63 44.7 12.08 41.4 4.51 12.64h7.81v-7.25l6.36-84.89 11.76-104.21 11.44-134.09 3.95-37.77 18.68-45.26 37.13-24.48 29 13.85 23.83 34.15-3.3 22.07-14.17 92.13-27.79 144.32-18.12 96.65 10.55-.001 12.08-12.08 48.89-64.91 82.15-102.68 36.24-40.75 42.28-45.02 27.14-21.42 51.3-.001 38.02 56.13-16.91 57.99-52.83 67.01-43.82 56.78-62.82 84.56-39.22 67.65 3.62 5.4 9.34-0.89 141.91-30.2 76.67-13.85 91.49-15.7 41.4 19.33 4.51 19.65-16.27 40.19-97.85 24.16-114.77 22.95-170.9 40.43-2.09 1.53 2.42 2.98 77 7.25 32.94 1.77 80.62-.001 150.12 11.2 39.22 25.93 23.52 31.73-3.95 24.16-60.4 30.77-81.5-19.33-190.23-45.26-65.46-16.27-9.02-.001v5.4l54.36 53.15 99.62 89.96 124.75 115.97 6.36 28.67-16.03 22.63-16.91-2.42-109.61-82.47-42.28-37.13-95.76-80.62-6.36-.001v8.46l22.07 32.29L919.09 1027.41l6.04 53.72-8.46 17.48-30.2 10.55-33.18-6.04-68.21-95.76-70.39-107.84-56.78-96.64-6.93 3.95-33.5 360.89-15.7 18.44-36.24 13.85-30.2-22.95-16.03-37.13 16.03-73.37 19.33-95.76 15.7-76.31 14.17-94.55 8.46-31.41-.56-2.09-6.93.89-71.28 97.85-108.68 146.5-85.77 91.81-20.54 8.14-35.6-18.44 3.3-32.94 19.89-29.32 118.71-150.9 71.6-93.58 46.23-54.04-.32-7.81-2.74-.001L205.29 929.4l-56.13 7.25-24.16-22.63 2.98-37.13 11.44-12.08 94.79-65.23-.32.32Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 32.25 142 55.5" fill="currentColor" preserveAspectRatio="xMidYMid meet">
|
||||
<path d="M84.848 34.137c-9.798 0-17.769 7.971-17.769 17.77s7.971 17.769 17.769 17.769 17.77-7.971 17.77-17.769-7.972-17.77-17.77-17.77zm0 28.876c-6.124 0-11.106-4.983-11.106-11.106s4.982-11.106 11.106-11.106c6.124 0 11.106 4.982 11.106 11.106S90.973 63.013 84.848 63.013z" />
|
||||
<path d="M60.315 34.206c-.607-.068-1.217-.104-1.827-.108-.304 0-.595.009-.893.014s-.594.033-.891.051c-1.197.094-2.382.299-3.541.611-2.329.629-4.574 1.723-6.515 3.277-1.97 1.57-3.548 3.575-4.611 5.859-.53 1.138-.921 2.336-1.165 3.567-.121.608-.21 1.222-.266 1.84-.02.307-.055.615-.059.921l-.011.459-.005.23v.19l.015 5.951.015 5.951.041 5.95h6.664l.042-5.95.015-5.952.015-5.951v-.182l.005-.142.008-.285c0-.191.028-.375.039-.564.036-.37.091-.738.165-1.102.146-.716.374-1.413.678-2.077.613-1.332 1.528-2.502 2.673-3.419 1.156-.932 2.541-1.628 4.038-2.042.757-.207 1.532-.344 2.314-.408.198-.011.395-.03.594-.037.199-.007.402-.013.595-.012.383 0 .76.025 1.142.06 1.518.153 2.989.619 4.318 1.368l3.326-5.776C65.108 35.263 62.753 34.484 60.315 34.206z" />
|
||||
<path d="M17.77 34.048C7.971 34.048 0 42.019 0 51.817s7.971 17.77 17.77 17.77h5.844v-6.664H17.77c-6.124 0-11.106-4.982-11.106-11.106s4.982-11.106 11.106-11.106 11.132 4.982 11.132 11.106v16.365c0 6.084-4.954 11.039-11.023 11.103-2.904-.024-5.681-1.191-7.729-3.25l-4.712 4.712c3.266 3.283 7.691 5.151 12.321 5.201v.003c.04 0 .08 0 .119 0h.125v-.003c9.659-.131 17.48-8.005 17.525-17.686l.006-16.881C35.302 41.785 27.422 34.048 17.77 34.048z" />
|
||||
<path d="M124.083 34.137c-9.798 0-17.769 7.971-17.769 17.77s7.971 17.769 17.769 17.769h6.08v-6.663h-6.08c-6.124 0-11.106-4.983-11.106-11.106s4.982-11.106 11.106-11.106c5.799 0 10.572 4.468 11.062 10.143h-.01v34.12h6.664V51.907C141.797 42.108 133.881 34.137 124.083 34.137z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 129 91" fill="currentColor" preserveAspectRatio="xMidYMid meet">
|
||||
<g>
|
||||
<rect x="18.292" y="0" width="18.293" height="18.123" />
|
||||
<rect x="91.473" y="0" width="18.293" height="18.123" />
|
||||
<rect x="18.292" y="18.121" width="36.586" height="18.123" />
|
||||
<rect x="73.181" y="18.121" width="36.586" height="18.123" />
|
||||
<rect x="18.292" y="36.243" width="91.476" height="18.122" />
|
||||
<rect x="18.292" y="54.37" width="18.293" height="18.123" />
|
||||
<rect x="54.883" y="54.37" width="18.293" height="18.123" />
|
||||
<rect x="91.473" y="54.37" width="18.293" height="18.123" />
|
||||
<rect x="0" y="72.504" width="54.89" height="18.123" />
|
||||
<rect x="73.181" y="72.504" width="54.89" height="18.123" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 778 B |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.89 595.28" fill="currentColor" preserveAspectRatio="xMidYMid meet">
|
||||
<g>
|
||||
<polygon points="557.09,211.99 565.4,538.36 631.96,538.36 640.28,93.18" />
|
||||
<polygon points="640.28,56.91 538.72,56.91 379.35,284.53 430.13,357.05" />
|
||||
<polygon points="201.61,538.36 303.17,538.36 353.96,465.84 303.17,393.31" />
|
||||
<polygon points="201.61,211.99 430.13,538.36 531.69,538.36 303.17,211.99" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 465 B |
@@ -0,0 +1,19 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconAnthropicRaw from '@assets/icons/anthropic.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconBrandAnthropicProps = Pick<IconComponentProps, 'size' | 'color'>;
|
||||
|
||||
export const IconBrandAnthropic = (props: IconBrandAnthropicProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
<IconAnthropicRaw
|
||||
height={size}
|
||||
width={size}
|
||||
color={props.color ?? 'currentColor'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconBrandGroqRaw from '@assets/icons/groq.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconBrandGroqProps = Pick<IconComponentProps, 'size' | 'color'>;
|
||||
|
||||
export const IconBrandGroq = (props: IconBrandGroqProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
<IconBrandGroqRaw
|
||||
height={size}
|
||||
width={size}
|
||||
color={props.color ?? 'currentColor'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconBrandMistralRaw from '@assets/icons/mistral.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconBrandMistralProps = Pick<IconComponentProps, 'size' | 'color'>;
|
||||
|
||||
export const IconBrandMistral = (props: IconBrandMistralProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
<IconBrandMistralRaw
|
||||
height={size}
|
||||
width={size}
|
||||
color={props.color ?? 'currentColor'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import IconBrandXaiRaw from '@assets/icons/xai.svg?react';
|
||||
import { type IconComponentProps } from '@ui/display/icon/types/IconComponent';
|
||||
|
||||
type IconBrandXaiProps = Pick<IconComponentProps, 'size' | 'color'>;
|
||||
|
||||
export const IconBrandXai = (props: IconBrandXaiProps) => {
|
||||
const theme = useTheme();
|
||||
const size = props.size ?? theme.icon.size.lg;
|
||||
|
||||
return (
|
||||
<IconBrandXaiRaw
|
||||
height={size}
|
||||
width={size}
|
||||
color={props.color ?? 'currentColor'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -46,6 +46,7 @@ export {
|
||||
IconBrandGoogle,
|
||||
IconBrandGraphql,
|
||||
IconBrandLinkedin,
|
||||
IconBrandOpenai,
|
||||
IconBrandX,
|
||||
IconBriefcase,
|
||||
IconBroadcast,
|
||||
@@ -325,6 +326,7 @@ export {
|
||||
IconSettings,
|
||||
IconSettings2,
|
||||
IconSettingsAutomation,
|
||||
IconSettingsBolt,
|
||||
IconShare,
|
||||
IconShield,
|
||||
IconSitemap,
|
||||
|
||||
@@ -34,6 +34,10 @@ export { CommandBlock } from './command-block/components/CommandBlock';
|
||||
export type { IconProps } from './icon/components/Icon';
|
||||
export { Icon } from './icon/components/Icon';
|
||||
export { IconAddressBook } from './icon/components/IconAddressBook';
|
||||
export { IconBrandAnthropic } from './icon/components/IconBrandAnthropic';
|
||||
export { IconBrandGroq } from './icon/components/IconBrandGroq';
|
||||
export { IconBrandMistral } from './icon/components/IconBrandMistral';
|
||||
export { IconBrandXai } from './icon/components/IconBrandXai';
|
||||
export { IconChartBarHorizontal } from './icon/components/IconChartBarHorizontal';
|
||||
export { IconGmail } from './icon/components/IconGmail';
|
||||
export { IconGoogle } from './icon/components/IconGoogle';
|
||||
@@ -117,6 +121,7 @@ export {
|
||||
IconBrandGoogle,
|
||||
IconBrandGraphql,
|
||||
IconBrandLinkedin,
|
||||
IconBrandOpenai,
|
||||
IconBrandX,
|
||||
IconBriefcase,
|
||||
IconBroadcast,
|
||||
@@ -396,6 +401,7 @@ export {
|
||||
IconSettings,
|
||||
IconSettings2,
|
||||
IconSettingsAutomation,
|
||||
IconSettingsBolt,
|
||||
IconShare,
|
||||
IconShield,
|
||||
IconSitemap,
|
||||
|
||||
Reference in New Issue
Block a user