chore: update model dropdown and my model with collapsible chevron

This commit is contained in:
Faisal Amir 2024-08-15 21:21:10 +07:00 committed by Louis
parent 3de7eda813
commit 3b87b7ae3a
22 changed files with 716 additions and 368 deletions

View File

@ -25,6 +25,10 @@ export enum InferenceEngine {
triton_trtllm = 'triton_trtllm',
nitro_tensorrt_llm = 'nitro-tensorrt-llm',
cohere = 'cohere',
nvdia = 'nvidia',
cortex_llamacpp = 'cortex.llamacpp',
cortex_onnx = 'cortex.onnx',
cortex_tensorrtllm = 'cortex.tensorrtllm',
}
export type ModelArtifact = {

View File

@ -1,11 +1,25 @@
import { useState, useMemo, useEffect, useCallback, useRef } from 'react'
import Image from 'next/image'
import { InferenceEngine } from '@janhq/core'
import { Badge, Input, ScrollArea, Select, useClickOutside } from '@janhq/joi'
import {
Badge,
Button,
Input,
ScrollArea,
Select,
useClickOutside,
} from '@janhq/joi'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import { ChevronDownIcon, DownloadCloudIcon, XIcon } from 'lucide-react'
import {
ChevronDownIcon,
ChevronUpIcon,
DownloadCloudIcon,
XIcon,
} from 'lucide-react'
import { twMerge } from 'tailwind-merge'
import ProgressCircle from '@/containers/Loader/ProgressCircle'
@ -22,6 +36,13 @@ import useUpdateModelParameters from '@/hooks/useUpdateModelParameters'
import { formatDownloadPercentage, toGibibytes } from '@/utils/converter'
import {
getLogoEngine,
getTitleByEngine,
localEngines,
priorityEngine,
} from '@/utils/modelEngine'
import { extensionManager } from '@/extension'
import { inActiveEngineProviderAtom } from '@/helpers/atoms/Extension.atom'
@ -29,6 +50,7 @@ import {
configuredModelsAtom,
getDownloadingModelAtom,
selectedModelAtom,
showEngineListModelAtom,
} from '@/helpers/atoms/Model.atom'
import {
activeThreadAtom,
@ -41,14 +63,6 @@ type Props = {
disabled?: boolean
}
const engineHasLogo = [
InferenceEngine.anthropic,
InferenceEngine.cohere,
InferenceEngine.martian,
InferenceEngine.mistral,
InferenceEngine.openai,
]
const ModelDropdown = ({
disabled,
chatInputMode,
@ -81,6 +95,10 @@ const ModelDropdown = ({
toggle,
])
const [showEngineListModel, setShowEngineListModel] = useAtom(
showEngineListModelAtom
)
const filteredDownloadedModels = useMemo(
() =>
configuredModels
@ -92,16 +110,10 @@ const ModelDropdown = ({
return e.engine
}
if (searchFilter === 'local') {
return (
e.engine === InferenceEngine.nitro ||
e.engine === InferenceEngine.nitro_tensorrt_llm
)
return localEngines.includes(e.engine)
}
if (searchFilter === 'remote') {
return (
e.engine !== InferenceEngine.nitro &&
e.engine !== InferenceEngine.nitro_tensorrt_llm
)
return !localEngines.includes(e.engine)
}
})
.sort((a, b) => a.name.localeCompare(b.name))
@ -230,10 +242,37 @@ const ModelDropdown = ({
.filter((x) => !inActiveEngineProvider.includes(x.engine))
.map((x) => x.engine)
const groupByEngine = findByEngine.filter(function (item, index) {
if (findByEngine.indexOf(item) === index)
return item !== InferenceEngine.nitro
})
const groupByEngine = findByEngine
.filter(function (item, index) {
if (findByEngine.indexOf(item) === index) return item
})
.sort((a, b) => {
if (priorityEngine.includes(a) && priorityEngine.includes(b)) {
return priorityEngine.indexOf(a) - priorityEngine.indexOf(b)
} else if (priorityEngine.includes(a)) {
return -1
} else if (priorityEngine.includes(b)) {
return 1
} else {
return 0 // Leave the rest in their original order
}
})
const getEngineStatusReady: InferenceEngine[] = extensionHasSettings
?.filter((e) => e.apiKey.length > 0)
.map((x) => x.provider as InferenceEngine)
useEffect(() => {
setShowEngineListModel((prev) => [
...prev,
...(getEngineStatusReady as InferenceEngine[]),
])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setShowEngineListModel, extensionHasSettings])
const isDownloadALocalModel = downloadedModels.some((x) =>
localEngines.includes(x.engine)
)
if (strictedThread && !activeThread) {
return null
@ -312,161 +351,22 @@ const ModelDropdown = ({
</div>
</div>
<ScrollArea className="h-[calc(100%-36px)] w-full">
{searchFilter !== 'remote' && (
<div className="relative w-full">
<div className="mt-2">
<h6 className="mb-1 mt-3 px-3 font-medium text-[hsla(var(--text-secondary))]">
Cortex
</h6>
</div>
{filteredDownloadedModels
.filter((x) => {
if (searchText.length === 0) {
return downloadedModels.find((c) => c.id === x.id)
} else {
return x
}
})
.filter((x) => x.engine === InferenceEngine.nitro).length !==
0 ? (
<ul className="pb-2">
{filteredDownloadedModels
? filteredDownloadedModels
.filter((x) => x.engine === InferenceEngine.nitro)
.filter((x) => {
if (searchText.length === 0) {
return downloadedModels.find((c) => c.id === x.id)
} else {
return x
}
})
.map((model) => {
const isDownloading = downloadingModels.some(
(md) => md.id === model.id
)
const isdDownloaded = downloadedModels.some(
(c) => c.id === model.id
)
return (
<li
key={model.id}
className="flex items-center justify-between gap-4 px-3 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]"
onClick={() => {
if (isdDownloaded) {
onClickModelItem(model.id)
}
}}
>
<div className="flex items-center gap-2">
<p
className={twMerge(
'line-clamp-1',
!isdDownloaded &&
'text-[hsla(var(--text-secondary))]'
)}
title={model.name}
>
{model.name}
</p>
<ModelLabel
metadata={model.metadata}
compact
/>
</div>
<div className="flex items-center gap-2 text-[hsla(var(--text-tertiary))]">
{!isdDownloaded && (
<span className="font-medium">
{toGibibytes(model.metadata.size)}
</span>
)}
{!isDownloading && !isdDownloaded ? (
<DownloadCloudIcon
size={18}
className="cursor-pointer text-[hsla(var(--app-link))]"
onClick={() => downloadModel(model)}
/>
) : (
Object.values(downloadStates)
.filter((x) => x.modelId === model.id)
.map((item) => (
<ProgressCircle
key={item.modelId}
percentage={
formatDownloadPercentage(
item?.percent,
{
hidePercentage: true,
}
) as number
}
size={100}
/>
))
)}
</div>
</li>
)
})
: null}
</ul>
) : (
<ul className="pb-2">
{featuredModel.map((model) => {
const isDownloading = downloadingModels.some(
(md) => md.id === model.id
)
return (
<li
key={model.id}
className="flex items-center justify-between gap-4 px-3 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]"
>
<div className="flex items-center gap-2">
<p
className="line-clamp-1 text-[hsla(var(--text-secondary))]"
title={model.name}
>
{model.name}
</p>
<ModelLabel metadata={model.metadata} compact />
</div>
<div className="flex items-center gap-2 text-[hsla(var(--text-tertiary))]">
<span className="font-medium">
{toGibibytes(model.metadata.size)}
</span>
{!isDownloading ? (
<DownloadCloudIcon
size={18}
className="cursor-pointer text-[hsla(var(--app-link))]"
onClick={() => downloadModel(model)}
/>
) : (
Object.values(downloadStates)
.filter((x) => x.modelId === model.id)
.map((item) => (
<ProgressCircle
key={item.modelId}
percentage={
formatDownloadPercentage(item?.percent, {
hidePercentage: true,
}) as number
}
size={100}
/>
))
)}
</div>
</li>
)
})}
</ul>
)}
</div>
)}
{groupByEngine.map((engine, i) => {
const apiKey =
extensionHasSettings.filter((x) => x.provider === engine)[0]
?.apiKey.length > 1
const apiKey = !localEngines.includes(engine)
? extensionHasSettings.filter((x) => x.provider === engine)[0]
?.apiKey.length > 1
: true
const engineLogo = getLogoEngine(engine as InferenceEngine)
const showModel = showEngineListModel.includes(engine)
const onClickChevron = () => {
if (showModel) {
setShowEngineListModel((prev) =>
prev.filter((item) => item !== engine)
)
} else {
setShowEngineListModel((prev) => [...prev, engine])
}
}
return (
<div
className="relative w-full border-t border-[hsla(var(--app-border))] first:border-t-0"
@ -474,57 +374,221 @@ const ModelDropdown = ({
>
<div className="mt-2">
<div className="flex items-center justify-between px-3">
<h6 className="mb-1 mt-3 font-medium capitalize text-[hsla(var(--text-secondary))]">
{engine}
</h6>
<div className="-mr-2">
<SetupRemoteModel engine={engine} />
<div
className="flex cursor-pointer items-center gap-2 py-1"
onClick={onClickChevron}
>
{engineLogo && (
<Image
className="h-6 w-6 flex-shrink-0"
width={48}
height={48}
src={engineLogo}
alt="logo"
/>
)}
<h6 className="font-medium text-[hsla(var(--text-secondary))]">
{getTitleByEngine(engine)}
</h6>
</div>
<div className="-mr-2 flex gap-1">
{!localEngines.includes(engine) && (
<SetupRemoteModel engine={engine} />
)}
{!showModel ? (
<Button theme="icon" onClick={onClickChevron}>
<ChevronDownIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
</Button>
) : (
<Button theme="icon" onClick={onClickChevron}>
<ChevronUpIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
</Button>
)}
</div>
</div>
{engine === InferenceEngine.nitro &&
!isDownloadALocalModel &&
showModel && (
<>
{!searchText.length ? (
<ul className="pb-2">
{featuredModel.map((model) => {
const isDownloading = downloadingModels.some(
(md) => md.id === model.id
)
return (
<li
key={model.id}
className="flex items-center justify-between gap-4 px-3 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]"
>
<div className="flex items-center gap-2">
<p
className="line-clamp-1 text-[hsla(var(--text-secondary))]"
title={model.name}
>
{model.name}
</p>
<ModelLabel
metadata={model.metadata}
compact
/>
</div>
<div className="flex items-center gap-2 text-[hsla(var(--text-tertiary))]">
<span className="font-medium">
{toGibibytes(model.metadata.size)}
</span>
{!isDownloading ? (
<DownloadCloudIcon
size={18}
className="cursor-pointer text-[hsla(var(--app-link))]"
onClick={() => downloadModel(model)}
/>
) : (
Object.values(downloadStates)
.filter((x) => x.modelId === model.id)
.map((item) => (
<ProgressCircle
key={item.modelId}
percentage={
formatDownloadPercentage(
item?.percent,
{
hidePercentage: true,
}
) as number
}
size={100}
/>
))
)}
</div>
</li>
)
})}
</ul>
) : (
<>
{filteredDownloadedModels
.filter(
(x) => x.engine === InferenceEngine.nitro
)
.filter((x) => {
if (searchText.length === 0) {
return downloadedModels.find(
(c) => c.id === x.id
)
} else {
return x
}
})
.map((model) => {
const isDownloading = downloadingModels.some(
(md) => md.id === model.id
)
const isdDownloaded = downloadedModels.some(
(c) => c.id === model.id
)
return (
<li
key={model.id}
className="flex items-center justify-between gap-4 px-3 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]"
onClick={() => {
if (isdDownloaded) {
onClickModelItem(model.id)
}
}}
>
<div className="flex items-center gap-2">
<p
className={twMerge(
'line-clamp-1',
!isdDownloaded &&
'text-[hsla(var(--text-secondary))]'
)}
title={model.name}
>
{model.name}
</p>
<ModelLabel
metadata={model.metadata}
compact
/>
</div>
<div className="flex items-center gap-2 text-[hsla(var(--text-tertiary))]">
{!isdDownloaded && (
<span className="font-medium">
{toGibibytes(model.metadata.size)}
</span>
)}
{!isDownloading && !isdDownloaded ? (
<DownloadCloudIcon
size={18}
className="cursor-pointer text-[hsla(var(--app-link))]"
onClick={() => downloadModel(model)}
/>
) : (
Object.values(downloadStates)
.filter(
(x) => x.modelId === model.id
)
.map((item) => (
<ProgressCircle
key={item.modelId}
percentage={
formatDownloadPercentage(
item?.percent,
{
hidePercentage: true,
}
) as number
}
size={100}
/>
))
)}
</div>
</li>
)
})}
</>
)}
</>
)}
<ul className="pb-2">
{filteredDownloadedModels
.filter((x) => x.engine === engine)
.filter((y) => {
if (localEngines.includes(y.engine)) {
return downloadedModels.find((c) => c.id === y.id)
} else {
return y
}
})
.map((model) => {
if (!showModel) return null
return (
<li
key={model.id}
className={twMerge(
'cursor-pointer px-3 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]',
!apiKey &&
model.engine !==
InferenceEngine.nitro_tensorrt_llm &&
'cursor-default text-[hsla(var(--text-tertiary))]'
!apiKey
? 'cursor-disabled text-[hsla(var(--text-tertiary))]'
: 'text-[hsla(var(--text-secondary))]'
)}
onClick={() => {
if (
apiKey ||
model.engine ===
InferenceEngine.nitro_tensorrt_llm
) {
onClickModelItem(model.id)
}
onClickModelItem(model.id)
}}
>
<div className="flex flex-shrink-0 gap-x-2">
{engineHasLogo.map((x) => {
if (x === model.engine) {
return (
<div
className="relative flex-shrink-0 overflow-hidden rounded-full"
key={x}
>
<img
src={`images/ModelProvider/${x}.svg`}
alt="Model Provider"
width={20}
height={20}
className="object-cover"
/>
</div>
)
}
})}
<p className="line-clamp-1" title={model.name}>
<p className="line-clamp-1 " title={model.name}>
{model.name}
</p>
</div>

View File

@ -4,10 +4,12 @@ import { InferenceEngine } from '@janhq/core'
import { Button } from '@janhq/joi'
import { useSetAtom } from 'jotai'
import { SettingsIcon } from 'lucide-react'
import { SettingsIcon, PlusIcon } from 'lucide-react'
import { MainViewState } from '@/constants/screens'
import { localEngines } from '@/utils/modelEngine'
import { extensionManager } from '@/extension'
import { mainViewStateAtom } from '@/helpers/atoms/App.atom'
import { selectedSettingAtom } from '@/helpers/atoms/Setting.atom'
@ -72,6 +74,11 @@ const SetupRemoteModel = ({ engine }: Props) => {
)
}
const apiKey = !localEngines.includes(engine)
? extensionHasSettings.filter((x) => x.provider === engine)[0]?.apiKey
.length > 1
: true
return (
<Button
theme="icon"
@ -80,10 +87,14 @@ const SetupRemoteModel = ({ engine }: Props) => {
onSetupItemClick(engine)
}}
>
<SettingsIcon
size={14}
className="text-hsla(var(--app-text-sencondary))"
/>
{apiKey ? (
<SettingsIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
) : (
<PlusIcon size={14} className="text-[hsla(var(--text-secondary))]" />
)}
</Button>
)
}

View File

@ -1,6 +1,8 @@
import { ImportingModel, Model } from '@janhq/core'
import { ImportingModel, Model, InferenceEngine } from '@janhq/core'
import { atom } from 'jotai'
import { localEngines } from '@/utils/modelEngine'
export const stateModel = atom({ state: 'start', loading: false, model: '' })
export const activeAssistantModelAtom = atom<Model | undefined>(undefined)
@ -132,3 +134,5 @@ export const updateImportingModelAtom = atom(
)
export const selectedModelAtom = atom<Model | undefined>(undefined)
export const showEngineListModelAtom = atom<InferenceEngine[]>(localEngines)

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,14 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16 13.3371C16 14.1449 15.5635 14.8 15.0254 14.8H0.974573C0.436327 14.8 0 14.1449 0 13.3371V3.46307C0 2.65488 0.436327 2 0.974573 2H15.0254C15.5635 2 16 2.65488 16 3.46307V13.3371Z" fill="#98CBF9"/>
<path d="M15.0505 2.33301H1.36558C0.841125 2.33301 0.41626 2.97078 0.41626 3.75796V13.3751C0.41626 14.1618 0.841125 14.7998 1.36558 14.7998H15.0252C15.5634 14.7998 16 14.1447 16 13.3369V3.75796C16 2.97078 15.5749 2.33301 15.0505 2.33301Z" fill="#98CBF9"/>
<path d="M0.959961 3.43996C0.959961 3.17486 1.17486 2.95996 1.43996 2.95996H14.56C14.8251 2.95996 15.04 3.17486 15.04 3.43996V13.36C15.04 13.6251 14.8251 13.84 14.56 13.84H1.43996C1.17486 13.84 0.959961 13.6251 0.959961 13.36V3.43996Z" stroke="#202020" stroke-width="0.32" stroke-miterlimit="10"/>
<path d="M1.92004 3.91992H14.08V12.8799H1.92004V3.91992Z" fill="#202020"/>
<path d="M5.11995 7.75984H3.19995V5.83984H5.11995V7.75984Z" fill="white"/>
<path d="M3.84009 7.75998H5.12009V6.47998H3.84009V7.75998Z" fill="#98CBF9"/>
<path d="M5.11995 7.75984H3.19995V5.83984H5.11995V7.75984Z" fill="white"/>
<path d="M3.84009 7.75998H5.12009V6.47998H3.84009V7.75998Z" fill="#98CBF9"/>
<path d="M12.8 7.75984H10.88V5.83984H12.8V7.75984Z" fill="white"/>
<path d="M11.5199 7.75998H12.7999V6.47998H11.5199V7.75998Z" fill="#98CBF9"/>
<path d="M12.8 11.5999H3.19995V9.67993H12.8V11.5999Z" fill="white"/>
<path d="M12.8001 11.5998H3.84009V10.3198H12.8001V11.5998Z" fill="#98CBF9"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="7" cy="7" r="3" fill="#18181B"/>
</svg>

After

Width:  |  Height:  |  Size: 148 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="16" height="16" rx="8" fill="url(#pattern0_3571_159)"/>
<defs>
<pattern id="pattern0_3571_159" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_3571_159" transform="scale(0.00497512)"/>
</pattern>
<image id="image0_3571_159" width="201" height="201" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMkAAADJCAMAAAC+GOY3AAAAQlBMVEX1TzX1TzX1TzX1TzX1TzX2WkL4e2j6p5r7sqf9083+9PL////+6eb5kYH4hnT7vbP3cFv8yMD93tn2ZU75nI31TzW9T89eAAAABXRSTlOPv2CAQLFEbcUAAAUSSURBVHic7ZzrmqMgDIbnsIra2mpb7/9Wt4c5dElQMGH57JPv90zlFXKQQN7e3l9DH2/v02vo00jgZCR4MhI8GQmejARPRoInI8GTkeDJSPBkJHgyEjwZCZ6MBE9GgicjwZOR4MlI8GQkeDKSKFW1a9q22920b1vX1/melY+kOhz3O6rWDXmel4vkMDIUX9o3WWBykFSuC3PcderVH5qBpDouYDwmRp1Fm2R5Pn5YDqoP1iapOSsPqT1rPlqVpGoSOK7qNJeYJsmQMiEPHSu1pyuS9LEW8qyTmkfWI3ErOK7qtFDUSKJ8L4ui5MO0SFaDXKVj90okEhClBaZDcpGAKKGokBxkINd4r+CMNUjOYffbja5+vPD64NowSisehArJKYRB0vdwsu/Eo1AgCQQSPts9hzJMsanISQZ+Pi6hvw9k/SfhMBRI2LU1zpkwnzBL15eYpOdGFZyQh9hp6YT+S0zCvN+I8MDZ1lE2ECkJMyVRcY6bStmHl5SETklkwGZQGtFIhCTMeGJ356ityCxFSELDdrwLok5PlBTLSM5kMAlpB01yRDFFRkJz4BSrlf23LxkJWSBp4Y24i4U4NCsRCVlciTZbay4vEQnxXKkZB5kUgfcSkRBHmrrOyasQFFhEJP4rHVN/oJJO6pNEJP440uOB/+Ul+HaUkBCDTXei/vLarx6MiMTfiFgxDPKZtnowIhI/NU82k4ku0PWxUZNkjbn6idt65yUh8Z3wmgiNQaIxCn9ejcRIHnodEj9Ar7F4DBINL4xJsiYyYpBoJE0YJAoZJAiJQlaPQiL+0oIhEX/9wpCQDat0PwxCIt0lmmBI6C5P8qSgkJDzXF2qpaCQ0Gpp6uYICglTOUhMI2FImEpQWmEdhmSi5wTSjtPgkDBF3CQUHJKKObyRgoJDwh7tSjgQCETCHS3Y7ZrYYI9EQr5S7oo9a45EQgP9Q23UoKBIqtBJtf1lOXeBIpmG8OnBU7NwowmLhD8ZFdbzaMFIQqayQZK0k8/QJEko2CQpKOAkCSjoJPEeDJ5kqiMvBeGTTNXMzdJtkUTe1doESdRFwG2QTNN50YltheTGMr/GtkNyXWN9KNPfGslV534MzczzaP2lCEhy09C77+YLodFinFvRkL8Kt0viz9f6XypMQjZm1v9UYRLfdRU60akg30wE94LKkpBCpeBgfVkSkqQJbjUWJSEb/Z3gx4qSkOKL5PpcSRJae5FcbypJQtN/yZWzgiT0Vr3obmY5EuZWvai9VzESplYhuOgwlSPhii6yHh8ZSIZ22XA5ENmUZCAZuuUyNlsGE7ZdUSe5F7gWmkCxm5XSpgXaJN+VupmmBYEtMWlfQmWS35Jj5wIsPd8bS3alfNIm+ad2yrFUAQ6F3jGqJKQIPPbPdeyqD2/pyXteqvZU48a5b52r69q5cW4zT3J1+UuKJDNl+SUJm2HcpUciADlpdLtTIykNokcy0wjqv4AUqf1mAdG0+HUosx2MUqTphRMP4twlDu0/0o2MqU06tToo3qSbrUSWsb+l2gJWOxdOaGar2v81x5dWZIPhYK68Vhm+fmOaJXeNam/hm7LsSMyXfm9HPZXn46ZceytDEzKYTK3Rc+4SnXvSsX4/XnK1q8++31UfnHNt2zbOHeoMa+pXpWu/ejISPBkJnowET0aCJyPBk5HgyUjwZCR4MhI8GQmejARPRoInI8GTkeDJSPBkJHgyEjwZCZ5eieTj8zX05y/Z/fRFIEbR7gAAAABJRU5ErkJggg=="/>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -1,11 +1,11 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3656_85650)">
<path d="M18.7344 31.9999C25.7504 31.9999 31.438 26.157 31.438 18.9494C31.438 11.7418 25.7504 5.89893 18.7344 5.89893C11.7184 5.89893 6.03076 11.7418 6.03076 18.9494C6.03076 26.157 11.7184 31.9999 18.7344 31.9999Z" fill="#FF563F"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.95491 20.3753C9.93698 24.3573 16.3932 24.3573 20.3753 20.3753C24.3573 16.3932 24.3573 9.93698 20.3753 5.95491C16.3932 1.97285 9.93698 1.97285 5.95491 5.95491C1.97285 9.93698 1.97285 16.3932 5.95491 20.3753ZM22.4742 22.4742C17.3329 27.6155 8.99725 27.6155 3.85596 22.4742C-1.28532 17.3329 -1.28532 8.99725 3.85596 3.85596C8.99725 -1.28532 17.3329 -1.28532 22.4742 3.85596C27.6155 8.99725 27.6155 17.3329 22.4742 22.4742Z" fill="#0C0C0C"/>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3571_178)">
<path d="M9.36718 16C12.8752 16 15.719 13.0785 15.719 9.47471C15.719 5.87092 12.8752 2.94946 9.36718 2.94946C5.85918 2.94946 3.01538 5.87092 3.01538 9.47471C3.01538 13.0785 5.85918 16 9.36718 16Z" fill="#FF563F"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.97746 10.1876C4.96849 12.1786 8.1966 12.1786 10.1876 10.1876C12.1786 8.1966 12.1786 4.96849 10.1876 2.97746C8.1966 0.986425 4.96849 0.986425 2.97746 2.97746C0.986425 4.96849 0.986425 8.1966 2.97746 10.1876ZM11.2371 11.2371C8.66645 13.8077 4.49863 13.8077 1.92798 11.2371C-0.64266 8.66645 -0.64266 4.49863 1.92798 1.92798C4.49863 -0.64266 8.66645 -0.64266 11.2371 1.92798C13.8077 4.49863 13.8077 8.66645 11.2371 11.2371Z" fill="#0C0C0C"/>
</g>
<defs>
<clipPath id="clip0_3656_85650">
<rect width="32" height="32" fill="white"/>
<clipPath id="clip0_3571_178">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 973 B

After

Width:  |  Height:  |  Size: 951 B

View File

@ -0,0 +1,72 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.59752 3.00269C4.59223 3.00269 4.58695 3.00269 4.58167 3.00269L4.56055 4.73761C4.56583 4.73761 4.57111 4.73761 4.57375 4.73761C5.71716 4.73761 6.60443 5.63808 8.53477 8.88084L8.6536 9.07889L8.66152 9.09209L9.74156 7.47599L9.73363 7.46279C9.48013 7.05084 9.23455 6.67059 9.00217 6.32466C8.73018 5.92328 8.47139 5.56414 8.21789 5.24462C6.94244 3.6206 5.87296 3.00269 4.59752 3.00269Z" fill="url(#paint0_linear_3534_109)"/>
<path d="M4.5817 3.00264C3.29833 3.00792 2.16548 3.8371 1.34423 5.10462C1.34159 5.10726 1.33894 5.11254 1.3363 5.11518L2.83885 5.93115C2.84149 5.92851 2.84413 5.92323 2.84677 5.92059C3.32473 5.20233 3.92153 4.74285 4.55793 4.73493C4.56321 4.73493 4.56849 4.73493 4.57113 4.73493L4.59226 3C4.59226 3.00264 4.58698 3.00264 4.5817 3.00264Z" fill="url(#paint1_linear_3534_109)"/>
<path d="M1.34687 5.10449C1.34423 5.10713 1.34159 5.11241 1.33895 5.11505C0.800247 5.95215 0.398864 6.97673 0.182328 8.08582C0.182328 8.0911 0.179688 8.09638 0.179688 8.09902L1.86972 8.49776C1.86972 8.49248 1.87236 8.4872 1.87236 8.48456C2.05193 7.51015 2.39786 6.60968 2.84413 5.93366C2.84677 5.93102 2.84941 5.92574 2.85205 5.9231L1.34687 5.10449Z" fill="url(#paint2_linear_3534_109)"/>
<path d="M1.87249 8.48444L0.182451 8.08569C0.182451 8.09097 0.17981 8.09626 0.17981 8.0989C0.0609798 8.70889 0.000244141 9.32681 0.000244141 9.94737C0.000244141 9.95265 0.000244141 9.95793 0.000244141 9.96322L1.73253 10.119C1.73253 10.1137 1.73253 10.1085 1.73253 10.1032C1.73253 10.0662 1.73253 10.0319 1.73253 9.9949C1.73253 9.49317 1.78006 8.99145 1.86984 8.50028C1.86984 8.49236 1.86984 8.48972 1.87249 8.48444Z" fill="url(#paint3_linear_3534_109)"/>
<path d="M1.78525 10.6581C1.75356 10.4785 1.73772 10.299 1.73244 10.1194C1.73244 10.1141 1.73244 10.1088 1.73244 10.1036L0.000152346 9.94775C0.000152346 9.95304 0.000152346 9.95832 0.000152346 9.9636C-0.00248833 10.3254 0.0292 10.6871 0.0952169 11.0463C0.0952169 11.0516 0.0978575 11.0568 0.0978575 11.0595L1.78789 10.6713C1.78525 10.6687 1.78525 10.6634 1.78525 10.6581Z" fill="url(#paint4_linear_3534_109)"/>
<path d="M2.17871 11.5531C1.99122 11.3472 1.85655 11.0514 1.78789 10.6712C1.78789 10.6659 1.78525 10.6606 1.78525 10.658L0.0952148 11.0461C0.0952148 11.0514 0.0978555 11.0567 0.0978555 11.0593C0.224608 11.7301 0.475472 12.2873 0.834605 12.7098C0.837245 12.7124 0.839886 12.7177 0.845167 12.7203L2.18927 11.5637C2.18663 11.5611 2.18399 11.5558 2.17871 11.5531Z" fill="url(#paint5_linear_3534_109)"/>
<path d="M7.18547 6.75244C6.16617 8.31308 5.55089 9.29013 5.55089 9.29013C4.19358 11.4132 3.72618 11.8886 2.97095 11.8886C2.65671 11.8886 2.39264 11.7777 2.19195 11.5611C2.18931 11.5585 2.18403 11.5532 2.18139 11.5506L0.83728 12.7072C0.839921 12.7098 0.842562 12.7151 0.847843 12.7177C1.34429 13.2934 2.04143 13.6129 2.90757 13.6129C4.21735 13.6129 5.15743 12.9976 6.83162 10.0771C6.83162 10.0771 7.52876 8.8465 8.00936 7.99884C7.70568 7.52616 7.43634 7.11157 7.18547 6.75244Z" fill="#0082FB"/>
<path d="M9.00218 4.29126C8.99954 4.2939 8.99426 4.29918 8.99161 4.30182C8.71698 4.60022 8.4582 4.91446 8.2179 5.2419C8.46876 5.56143 8.73019 5.92056 9.00218 6.32458C9.3217 5.83077 9.6201 5.43203 9.91321 5.12571C9.91585 5.12307 9.92113 5.11779 9.92377 5.11515L9.00218 4.29126Z" fill="url(#paint6_linear_3534_109)"/>
<path d="M13.9456 4.13818C13.2353 3.42255 12.3876 3.00269 11.4845 3.00269C10.5312 3.00269 9.72581 3.52554 9.00226 4.29134C8.99962 4.29398 8.99434 4.29926 8.9917 4.3019L9.9133 5.12315C9.91594 5.12051 9.92122 5.11523 9.92386 5.11259C10.4018 4.61614 10.8639 4.37056 11.3736 4.37056C11.9255 4.37056 12.4404 4.62934 12.8867 5.08354C12.8893 5.08618 12.8946 5.09146 12.8973 5.0941L13.9562 4.14874C13.9535 4.1461 13.9483 4.14346 13.9456 4.13818Z" fill="#0082FB"/>
<path d="M15.9974 9.72058C15.9578 7.42055 15.1524 5.36346 13.9535 4.14875C13.9509 4.14611 13.9456 4.14082 13.9429 4.13818L12.884 5.08355C12.8867 5.08619 12.892 5.09147 12.8946 5.09411C13.7951 6.01835 14.413 7.73479 14.4684 9.72058C14.4684 9.72586 14.4684 9.73114 14.4684 9.73642H15.9974C16 9.73114 15.9974 9.72586 15.9974 9.72058Z" fill="url(#paint7_linear_3534_109)"/>
<path d="M16 9.7363C16 9.73102 16 9.72574 16 9.72046H14.4711C14.4711 9.72574 14.4711 9.73102 14.4711 9.7363C14.4737 9.82873 14.4737 9.92379 14.4737 10.0162C14.4737 10.5576 14.3919 10.9959 14.2281 11.3101C14.2255 11.3154 14.2229 11.3207 14.2202 11.3234L15.3583 12.5064C15.361 12.5011 15.3636 12.4985 15.3663 12.4932C15.7809 11.8568 15.9974 10.9721 15.9974 9.89738C16 9.84457 16 9.78912 16 9.7363Z" fill="url(#paint8_linear_3534_109)"/>
<path d="M14.2282 11.3101C14.2256 11.3153 14.2229 11.3206 14.2203 11.3233C14.0777 11.59 13.8744 11.7669 13.6077 11.8435L14.1279 13.4781C14.1965 13.4543 14.2626 13.4279 14.3286 13.3988C14.3471 13.3909 14.3682 13.3804 14.3867 13.3724C14.3972 13.3671 14.4078 13.3619 14.4184 13.3566C14.7643 13.1823 15.0627 12.9262 15.2871 12.6093C15.3003 12.5881 15.3162 12.5697 15.3294 12.5485C15.34 12.5353 15.3479 12.5195 15.3584 12.5063C15.3611 12.501 15.3637 12.4984 15.3664 12.4931L14.2282 11.3101Z" fill="url(#paint9_linear_3534_109)"/>
<path d="M13.2801 11.891C13.1164 11.8936 12.9527 11.862 12.8021 11.7986L12.2714 13.4728C12.5698 13.5758 12.8893 13.6207 13.2458 13.6207C13.5521 13.6233 13.8558 13.5758 14.1462 13.4781L13.626 11.8435C13.5125 11.8752 13.3963 11.891 13.2801 11.891Z" fill="url(#paint10_linear_3534_109)"/>
<path d="M12.216 11.3182C12.2133 11.3155 12.2107 11.3103 12.2054 11.3076L10.9828 12.5778C10.9854 12.5804 10.9907 12.5857 10.9934 12.5883C11.4185 13.0399 11.8252 13.3225 12.2846 13.4756L12.8154 11.8041C12.6226 11.7196 12.4325 11.569 12.216 11.3182Z" fill="url(#paint11_linear_3534_109)"/>
<path d="M12.2054 11.3074C11.8383 10.8823 11.3841 10.1719 10.6711 9.02588L9.73896 7.47316L9.73104 7.45996L8.651 9.07606L8.65892 9.08926L9.31909 10.1957C9.95814 11.2625 10.4784 12.0363 10.9827 12.575C10.9854 12.5776 10.9906 12.5829 10.9933 12.5855L12.2159 11.3154C12.2133 11.3154 12.208 11.3101 12.2054 11.3074Z" fill="url(#paint12_linear_3534_109)"/>
<defs>
<linearGradient id="paint0_linear_3534_109" x1="9.01739" y1="8.43763" x2="5.44023" y2="3.74298" gradientUnits="userSpaceOnUse">
<stop offset="0.0006" stop-color="#0867DF"/>
<stop offset="0.4539" stop-color="#0668E1"/>
<stop offset="0.8591" stop-color="#0064E0"/>
</linearGradient>
<linearGradient id="paint1_linear_3534_109" x1="2.04645" y1="5.39837" x2="4.50086" y2="3.5321" gradientUnits="userSpaceOnUse">
<stop offset="0.1323" stop-color="#0064DF"/>
<stop offset="0.9988" stop-color="#0064E0"/>
</linearGradient>
<linearGradient id="paint2_linear_3534_109" x1="1.0039" y1="8.12949" x2="1.98693" y2="5.65105" gradientUnits="userSpaceOnUse">
<stop offset="0.0147" stop-color="#0072EC"/>
<stop offset="0.6881" stop-color="#0064DF"/>
</linearGradient>
<linearGradient id="paint3_linear_3534_109" x1="0.869701" y1="9.91881" x2="0.983398" y2="8.40577" gradientUnits="userSpaceOnUse">
<stop offset="0.0731" stop-color="#007CF6"/>
<stop offset="0.9943" stop-color="#0072EC"/>
</linearGradient>
<linearGradient id="paint4_linear_3534_109" x1="0.932222" y1="10.741" x2="0.850892" y2="10.1348" gradientUnits="userSpaceOnUse">
<stop offset="0.0731" stop-color="#007FF9"/>
<stop offset="1" stop-color="#007CF6"/>
</linearGradient>
<linearGradient id="paint5_linear_3534_109" x1="0.884741" y1="10.8998" x2="1.39186" y2="11.9771" gradientUnits="userSpaceOnUse">
<stop offset="0.0731" stop-color="#007FF9"/>
<stop offset="1" stop-color="#0082FB"/>
</linearGradient>
<linearGradient id="paint6_linear_3534_109" x1="8.69922" y1="5.69107" x2="9.36998" y2="4.76191" gradientUnits="userSpaceOnUse">
<stop offset="0.2799" stop-color="#007FF8"/>
<stop offset="0.9141" stop-color="#0082FB"/>
</linearGradient>
<linearGradient id="paint7_linear_3534_109" x1="13.8102" y1="4.48732" x2="15.2122" y2="9.65292" gradientUnits="userSpaceOnUse">
<stop stop-color="#0082FB"/>
<stop offset="0.9995" stop-color="#0081FA"/>
</linearGradient>
<linearGradient id="paint8_linear_3534_109" x1="15.5531" y1="9.84896" x2="14.6655" y2="11.6455" gradientUnits="userSpaceOnUse">
<stop offset="0.0619" stop-color="#0081FA"/>
<stop offset="1" stop-color="#0080F9"/>
</linearGradient>
<linearGradient id="paint9_linear_3534_109" x1="13.9578" y1="12.5974" x2="14.7888" y2="12.0305" gradientUnits="userSpaceOnUse">
<stop stop-color="#027AF3"/>
<stop offset="1" stop-color="#0080F9"/>
</linearGradient>
<linearGradient id="paint10_linear_3534_109" x1="12.6527" y1="12.7075" x2="13.8086" y2="12.7075" gradientUnits="userSpaceOnUse">
<stop stop-color="#0377EF"/>
<stop offset="0.9994" stop-color="#0279F1"/>
</linearGradient>
<linearGradient id="paint11_linear_3534_109" x1="11.6481" y1="12.0716" x2="12.4784" y2="12.5608" gradientUnits="userSpaceOnUse">
<stop offset="0.0019" stop-color="#0471E9"/>
<stop offset="1" stop-color="#0377EF"/>
</linearGradient>
<linearGradient id="paint12_linear_3534_109" x1="9.11475" y1="8.47073" x2="11.7718" y2="11.8166" gradientUnits="userSpaceOnUse">
<stop offset="0.2765" stop-color="#0867DF"/>
<stop offset="1" stop-color="#0471E9"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

@ -1,32 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256px" height="233px" viewBox="0 0 256 233" version="1.1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid">
<title>Mistral AI</title>
<g>
<rect fill="#000000" x="186.181818" y="0" width="46.5454545" height="46.5454545"></rect>
<rect fill="#F7D046" x="209.454545" y="0" width="46.5454545" height="46.5454545"></rect>
<rect fill="#000000" x="0" y="0" width="46.5454545" height="46.5454545"></rect>
<rect fill="#000000" x="0" y="46.5454545" width="46.5454545" height="46.5454545"></rect>
<rect fill="#000000" x="0" y="93.0909091" width="46.5454545" height="46.5454545"></rect>
<rect fill="#000000" x="0" y="139.636364" width="46.5454545" height="46.5454545"></rect>
<rect fill="#000000" x="0" y="186.181818" width="46.5454545" height="46.5454545"></rect>
<rect fill="#F7D046" x="23.2727273" y="0" width="46.5454545" height="46.5454545"></rect>
<rect fill="#F2A73B" x="209.454545" y="46.5454545" width="46.5454545" height="46.5454545"></rect>
<rect fill="#F2A73B" x="23.2727273" y="46.5454545" width="46.5454545" height="46.5454545"></rect>
<rect fill="#000000" x="139.636364" y="46.5454545" width="46.5454545" height="46.5454545"></rect>
<rect fill="#F2A73B" x="162.909091" y="46.5454545" width="46.5454545" height="46.5454545"></rect>
<rect fill="#F2A73B" x="69.8181818" y="46.5454545" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EE792F" x="116.363636" y="93.0909091" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EE792F" x="162.909091" y="93.0909091" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EE792F" x="69.8181818" y="93.0909091" width="46.5454545" height="46.5454545"></rect>
<rect fill="#000000" x="93.0909091" y="139.636364" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EB5829" x="116.363636" y="139.636364" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EE792F" x="209.454545" y="93.0909091" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EE792F" x="23.2727273" y="93.0909091" width="46.5454545" height="46.5454545"></rect>
<rect fill="#000000" x="186.181818" y="139.636364" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EB5829" x="209.454545" y="139.636364" width="46.5454545" height="46.5454545"></rect>
<rect fill="#000000" x="186.181818" y="186.181818" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EB5829" x="23.2727273" y="139.636364" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EA3326" x="209.454545" y="186.181818" width="46.5454545" height="46.5454545"></rect>
<rect fill="#EA3326" x="23.2727273" y="186.181818" width="46.5454545" height="46.5454545"></rect>
</g>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.909 2H10.7272V4.39719H12.909V2Z" fill="black"/>
<path d="M14.0001 2H11.8182V4.39719H14.0001V2Z" fill="#F7D046"/>
<path d="M4.18182 2H2V4.39719H4.18182V2Z" fill="black"/>
<path d="M4.18182 4.39722H2V6.79441H4.18182V4.39722Z" fill="black"/>
<path d="M4.18182 6.79419H2V9.19138H4.18182V6.79419Z" fill="black"/>
<path d="M4.18182 9.19165H2V11.5888H4.18182V9.19165Z" fill="black"/>
<path d="M4.18182 11.5889H2V13.9861H4.18182V11.5889Z" fill="black"/>
<path d="M5.27276 2H3.09094V4.39719H5.27276V2Z" fill="#F7D046"/>
<path d="M14.0001 4.39722H11.8182V6.79441H14.0001V4.39722Z" fill="#F2A73B"/>
<path d="M5.27276 4.39722H3.09094V6.79441H5.27276V4.39722Z" fill="#F2A73B"/>
<path d="M10.7272 4.39722H8.54541V6.79441H10.7272V4.39722Z" fill="black"/>
<path d="M11.8182 4.39722H9.63635V6.79441H11.8182V4.39722Z" fill="#F2A73B"/>
<path d="M7.45465 4.39722H5.27283V6.79441H7.45465V4.39722Z" fill="#F2A73B"/>
<path d="M9.63641 6.79419H7.45459V9.19138H9.63641V6.79419Z" fill="#EE792F"/>
<path d="M11.8182 6.79419H9.63635V9.19138H11.8182V6.79419Z" fill="#EE792F"/>
<path d="M7.45465 6.79419H5.27283V9.19138H7.45465V6.79419Z" fill="#EE792F"/>
<path d="M8.54547 9.19165H6.36365V11.5888H8.54547V9.19165Z" fill="black"/>
<path d="M9.63641 9.19165H7.45459V11.5888H9.63641V9.19165Z" fill="#EB5829"/>
<path d="M14.0001 6.79419H11.8182V9.19138H14.0001V6.79419Z" fill="#EE792F"/>
<path d="M5.27276 6.79419H3.09094V9.19138H5.27276V6.79419Z" fill="#EE792F"/>
<path d="M12.909 9.19165H10.7272V11.5888H12.909V9.19165Z" fill="black"/>
<path d="M14.0001 9.19165H11.8182V11.5888H14.0001V9.19165Z" fill="#EB5829"/>
<path d="M12.909 11.5889H10.7272V13.9861H12.909V11.5889Z" fill="black"/>
<path d="M5.27276 9.19165H3.09094V11.5888H5.27276V9.19165Z" fill="#EB5829"/>
<path d="M14.0001 11.5889H11.8182V13.9861H14.0001V11.5889Z" fill="#EA3326"/>
<path d="M5.27276 11.5889H3.09094V13.9861H5.27276V11.5889Z" fill="#EA3326"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,6 @@
<svg width="12" height="24" viewBox="0 0 12 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.0949 10.247L9.3209 0L2.0899 11.224L0.399902 13.753H3.6609H5.5149L1.7559 24L9.5209 12.776L11.2099 10.247H7.9489H6.0949Z" fill="#FFDC75"/>
<path d="M5.695 10.247L9.321 0L1.69 11.224L0 13.753H3.261H5.115L1.756 24L9.121 12.776L10.81 10.247H7.549H5.695Z" fill="#FEC928"/>
<path d="M0 13.753H1L2.69 11.224L9.321 0L1.69 11.224L0 13.753Z" fill="#EDA703"/>
<path d="M1.75586 23.9999L6.11486 13.7529H5.11486L1.75586 23.9999Z" fill="#EDA703"/>
</svg>

After

Width:  |  Height:  |  Size: 548 B

View File

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_3572_51)">
<path d="M5.966 6.15551V5.20179C6.06019 5.19591 6.15439 5.19002 6.24858 5.19002C8.86247 5.1076 10.5756 7.43891 10.5756 7.43891C10.5756 7.43891 8.72707 10.0057 6.7431 10.0057C6.47818 10.0057 6.21915 9.96449 5.97189 9.88207V6.9856C6.99036 7.10923 7.19641 7.55665 7.80279 8.57513L9.16272 7.43302C9.16272 7.43302 8.16779 6.13196 6.49584 6.13196C6.31923 6.12607 6.14261 6.13785 5.966 6.15551ZM5.966 3V4.42469L6.24858 4.40703C9.88095 4.2834 12.2535 7.38592 12.2535 7.38592C12.2535 7.38592 9.53361 10.6945 6.70189 10.6945C6.45463 10.6945 6.21326 10.671 5.97189 10.6297V11.5128C6.17205 11.5364 6.3781 11.554 6.57826 11.554C9.2157 11.554 11.1231 10.2059 12.9717 8.61634C13.2778 8.8636 14.5318 9.4582 14.7908 9.71723C13.0365 11.189 8.94489 12.3723 6.62536 12.3723C6.40165 12.3723 6.18971 12.3606 5.97777 12.337V13.5792H15.9977V3L5.966 3ZM5.966 9.88207V10.6356C3.52872 10.2 2.8517 7.66262 2.8517 7.66262C2.8517 7.66262 4.02324 6.36745 5.966 6.15551V6.97971H5.96011C4.94163 6.85608 4.14098 7.8098 4.14098 7.8098C4.14098 7.8098 4.59429 9.41699 5.966 9.88207ZM1.63895 7.55665C1.63895 7.55665 3.0813 5.4255 5.97189 5.20179V4.42469C2.76928 4.68372 0.00231934 7.39181 0.00231934 7.39181C0.00231934 7.39181 1.5683 11.9249 5.966 12.337V11.5128C2.73984 11.1125 1.63895 7.55665 1.63895 7.55665Z" fill="#76B900"/>
</g>
<defs>
<clipPath id="clip0_3572_51">
<rect width="16" height="10.5792" fill="white" transform="translate(0 3)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,14 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" rx="8" fill="white"/>
<mask id="mask0_3571_174" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="1" y="2" width="13" height="12">
<path d="M13.5 2H1.5V14H13.5V2Z" fill="white"/>
</mask>
<g mask="url(#mask0_3571_174)">
<path d="M1.57031 7.83424C1.92188 7.83424 3.28125 7.53084 3.98438 7.1324C4.6875 6.73396 4.6875 6.73396 6.14062 5.70271C7.9804 4.39708 9.28125 4.83424 11.4141 4.83424" fill="black"/>
<path d="M1.57031 7.83424C1.92188 7.83424 3.28125 7.53084 3.98438 7.1324C4.6875 6.73396 4.6875 6.73396 6.14062 5.70271C7.9804 4.39708 9.28125 4.83424 11.4141 4.83424" stroke="black" stroke-width="2.10937"/>
<path d="M13.4766 4.84758L9.87305 6.92808V2.76709L13.4766 4.84758Z" fill="black" stroke="black" stroke-width="0.0234375"/>
<path d="M1.5 7.83594C1.85156 7.83594 3.21094 8.13934 3.91406 8.53777C4.61719 8.93621 4.61719 8.93621 6.07031 9.96746C7.91009 11.2731 9.21094 10.8359 11.3438 10.8359" fill="black"/>
<path d="M1.5 7.83594C1.85156 7.83594 3.21094 8.13934 3.91406 8.53777C4.61719 8.93621 4.61719 8.93621 6.07031 9.96746C7.91009 11.2731 9.21094 10.8359 11.3438 10.8359" stroke="black" stroke-width="2.10937"/>
<path d="M13.4062 10.8229L9.80273 8.74243V12.9034L13.4062 10.8229Z" fill="black" stroke="black" stroke-width="0.0234375"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 146 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.09026 8.41933L3.72077 7.62985C1.24061 6.80348 0 6.3903 0 5.63033C0 4.87142 1.24061 4.45718 3.72077 3.63081L12.6938 0.639442C14.4393 0.0576106 15.3121 -0.233305 15.7727 0.227312C16.2333 0.687928 15.9424 1.56068 15.3616 3.30512L12.3692 12.2792C11.5428 14.7594 11.1296 16 10.3697 16C9.61076 16 9.19652 14.7594 8.37015 12.2792L7.57962 9.9108L12.1689 5.3215C12.3609 5.1227 12.4672 4.85645 12.4648 4.58008C12.4624 4.30372 12.3515 4.03935 12.1561 3.84392C11.9607 3.64849 11.6963 3.53764 11.4199 3.53524C11.1435 3.53284 10.8773 3.63908 10.6785 3.83108L6.09026 8.41933Z" fill="#4377E9"/>
</svg>

After

Width:  |  Height:  |  Size: 734 B

View File

@ -16,6 +16,8 @@ import useDeleteModel from '@/hooks/useDeleteModel'
import { toGibibytes } from '@/utils/converter'
import { localEngines } from '@/utils/modelEngine'
import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom'
type Props = {
@ -44,33 +46,10 @@ const MyModelList = ({ model }: Props) => {
}
}
const engineHasLogo = [
InferenceEngine.anthropic,
InferenceEngine.cohere,
InferenceEngine.martian,
InferenceEngine.mistral,
InferenceEngine.openai,
]
return (
<div className="border border-b-0 border-[hsla(var(--app-border))] bg-[hsla(var(--tertiary-bg))] p-4 first:rounded-t-lg last:rounded-b-lg last:border-b">
<div className="flex flex-col items-start justify-start gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="flex w-1/2 gap-x-8">
{engineHasLogo.map((x) => {
if (x === model.engine) {
return (
<div className="relative overflow-hidden rounded-full" key={x}>
<img
src={`images/ModelProvider/${x}.svg`}
alt="Model Provider"
width={24}
height={24}
className="object-cover"
/>
</div>
)
}
})}
<div className="flex w-full items-center justify-between">
<h6
className={twMerge(
@ -95,7 +74,7 @@ const MyModelList = ({ model }: Props) => {
</div>
</div>
{model.engine === InferenceEngine.nitro && (
{localEngines.includes(model.engine) && (
<div className="flex gap-x-4">
<Badge theme="secondary" className="sm:mr-16">
{toGibibytes(model.metadata.size)}

View File

@ -1,13 +1,20 @@
import { useCallback, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useDropzone } from 'react-dropzone'
import Image from 'next/image'
import { InferenceEngine } from '@janhq/core'
import { Button, ScrollArea } from '@janhq/joi'
import { useAtomValue, useSetAtom } from 'jotai'
import { UploadCloudIcon, UploadIcon } from 'lucide-react'
import { useAtom, useAtomValue, useSetAtom } from 'jotai'
import {
ChevronDownIcon,
ChevronUpIcon,
UploadCloudIcon,
UploadIcon,
} from 'lucide-react'
import { twMerge } from 'tailwind-merge'
@ -18,15 +25,32 @@ import SetupRemoteModel from '@/containers/SetupRemoteModel'
import useDropModelBinaries from '@/hooks/useDropModelBinaries'
import { setImportModelStageAtom } from '@/hooks/useImportModel'
import {
getLogoEngine,
getTitleByEngine,
localEngines,
priorityEngine,
} from '@/utils/modelEngine'
import MyModelList from './MyModelList'
import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom'
import { extensionManager } from '@/extension'
import {
downloadedModelsAtom,
showEngineListModelAtom,
} from '@/helpers/atoms/Model.atom'
const MyModels = () => {
const downloadedModels = useAtomValue(downloadedModelsAtom)
const setImportModelStage = useSetAtom(setImportModelStageAtom)
const { onDropModels } = useDropModelBinaries()
const [searchText, setSearchText] = useState('')
const [showEngineListModel, setShowEngineListModel] = useAtom(
showEngineListModelAtom
)
const [extensionHasSettings, setExtensionHasSettings] = useState<
{ name?: string; setting: string; apiKey: string; provider: string }[]
>([])
const filteredDownloadedModels = useMemo(
() =>
@ -52,11 +76,73 @@ const MyModels = () => {
setSearchText(input)
}, [])
useEffect(() => {
const getAllSettings = async () => {
const extensionsMenu: {
name?: string
setting: string
apiKey: string
provider: string
}[] = []
const extensions = extensionManager.getAll()
for (const extension of extensions) {
if (typeof extension.getSettings === 'function') {
const settings = await extension.getSettings()
if (
(settings && settings.length > 0) ||
(await extension.installationState()) !== 'NotRequired'
) {
extensionsMenu.push({
name: extension.productName,
setting: extension.name,
apiKey:
'apiKey' in extension && typeof extension.apiKey === 'string'
? extension.apiKey
: '',
provider:
'provider' in extension &&
typeof extension.provider === 'string'
? extension.provider
: '',
})
}
}
}
setExtensionHasSettings(extensionsMenu)
}
getAllSettings()
}, [])
const findByEngine = filteredDownloadedModels.map((x) => x.engine)
const groupByEngine = findByEngine.filter(function (item, index) {
if (findByEngine.indexOf(item) === index)
return item !== InferenceEngine.nitro
})
const groupByEngine = findByEngine
.filter(function (item, index) {
if (findByEngine.indexOf(item) === index) return item
})
.sort((a, b) => {
if (priorityEngine.includes(a) && priorityEngine.includes(b)) {
return priorityEngine.indexOf(a) - priorityEngine.indexOf(b)
} else if (priorityEngine.includes(a)) {
return -1
} else if (priorityEngine.includes(b)) {
return 1
} else {
return 0 // Leave the rest in their original order
}
})
const getEngineStatusReady: InferenceEngine[] = extensionHasSettings
?.filter((e) => e.apiKey.length > 0)
.map((x) => x.provider as InferenceEngine)
useEffect(() => {
setShowEngineListModel((prev) => [
...prev,
...(getEngineStatusReady as InferenceEngine[]),
])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [setShowEngineListModel, extensionHasSettings])
return (
<div {...getRootProps()} className="h-full w-full">
@ -97,39 +183,65 @@ const MyModels = () => {
</div>
<div className="relative w-full">
{filteredDownloadedModels.filter(
(x) => x.engine === InferenceEngine.nitro
).length !== 0 && (
<div className="my-6">
<div className="flex flex-col items-start justify-start gap-2 sm:flex-row sm:items-center sm:justify-between">
<h6 className="text-base font-semibold">Cortex</h6>
</div>
<div className="mt-2">
{filteredDownloadedModels
? filteredDownloadedModels
.filter((x) => x.engine === InferenceEngine.nitro)
.map((model) => {
return <MyModelList key={model.id} model={model} />
})
: null}
</div>
</div>
)}
{groupByEngine.map((engine, i) => {
const engineLogo = getLogoEngine(engine as InferenceEngine)
const showModel = showEngineListModel.includes(engine)
const onClickChevron = () => {
if (showModel) {
setShowEngineListModel((prev) =>
prev.filter((item) => item !== engine)
)
} else {
setShowEngineListModel((prev) => [...prev, engine])
}
}
return (
<div className="my-6" key={i}>
<div className="flex flex-col items-start justify-start gap-2 sm:flex-row sm:items-center sm:justify-between">
<h6 className="text-base font-semibold capitalize">
{engine}
</h6>
<SetupRemoteModel engine={engine} />
<div
className="mb-1 mt-3 flex cursor-pointer items-center gap-2"
onClick={onClickChevron}
>
{engineLogo && (
<Image
className="h-6 w-6 flex-shrink-0"
width={48}
height={48}
src={engineLogo}
alt="logo"
/>
)}
<h6 className="font-medium text-[hsla(var(--text-secondary))]">
{getTitleByEngine(engine)}
</h6>
</div>
<div className="flex gap-1">
{!localEngines.includes(engine) && (
<SetupRemoteModel engine={engine} />
)}
{!showModel ? (
<Button theme="icon" onClick={onClickChevron}>
<ChevronDownIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
</Button>
) : (
<Button theme="icon" onClick={onClickChevron}>
<ChevronUpIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
</Button>
)}
</div>
</div>
<div className="mt-2">
{filteredDownloadedModels
? filteredDownloadedModels
.filter((x) => x.engine === engine)
.map((model) => {
if (!showModel) return null
return <MyModelList key={model.id} model={model} />
})
: null}

66
web/utils/modelEngine.ts Normal file
View File

@ -0,0 +1,66 @@
import { InferenceEngine } from '@janhq/core'
export const getLogoEngine = (engine: InferenceEngine) => {
switch (engine) {
case 'anthropic':
return 'images/ModelProvider/anthropic.svg'
case 'nitro':
return 'images/ModelProvider/nitro.svg'
case 'cortex.llamacpp':
case 'cortex.onnx':
case 'cortex.tensorrtllm':
return 'images/ModelProvider/cortex.svg'
case 'mistral':
return 'images/ModelProvider/mistral.svg'
case 'martian':
return 'images/ModelProvider/martian.svg'
case 'openrouter':
return 'images/ModelProvider/openrouter.svg'
case 'openai':
return 'images/ModelProvider/openai.svg'
case 'groq':
return 'images/ModelProvider/groq.svg'
case 'triton_trtllm':
return 'images/ModelProvider/triton_trtllm.svg'
case 'cohere':
return 'images/ModelProvider/cohere.svg'
case 'nvidia':
return 'images/ModelProvider/nvidia.svg'
default:
return undefined
}
}
export const localEngines = [
InferenceEngine.nitro,
InferenceEngine.nitro_tensorrt_llm,
InferenceEngine.cortex_llamacpp,
InferenceEngine.cortex_onnx,
InferenceEngine.cortex_tensorrtllm,
]
export const getTitleByEngine = (engine: InferenceEngine) => {
switch (engine) {
case 'nitro':
return 'Llama.cpp (Nitro)'
case 'cortex.llamacpp':
return 'Llama.cpp (Cortex)'
case 'cortex.onnx':
return 'Onnx (Cortex)'
case 'cortex.tensorrtllm':
return 'TensorRT-LLM (Cortex)'
case 'openai':
return 'OpenAI'
case 'openrouter':
return 'OpenRouter'
default:
return engine.charAt(0).toUpperCase() + engine.slice(1)
}
}
export const priorityEngine = [
InferenceEngine.cortex_llamacpp,
InferenceEngine.cortex_onnx,
InferenceEngine.cortex_tensorrtllm,
InferenceEngine.nitro,
]