chore(UI): update experience model dropdown (#3342)

* chore(ui)/update-experience-model-dropdown

* chore: remove unused container engine avatar

* chore: remove unused import

* chore: name list click able and change icon plus if apikey not yet setup
This commit is contained in:
Faisal Amir 2024-08-12 15:02:50 +07:00 committed by GitHub
parent 23cd5fd397
commit 8b44613015
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 144 additions and 32 deletions

View File

@ -17,7 +17,6 @@ const CenterPanelContainer = ({ children }: PropsWithChildren) => {
<div
className={twMerge(
'flex h-full w-full',
!reduceTransparent && 'px-1.5',
mainViewState === MainViewState.Hub && 'pl-0',
!downloadedModels.length &&
mainViewState === MainViewState.Thread &&

View File

@ -1,22 +1,35 @@
import { useCallback, useEffect, useState } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import Image from 'next/image'
import { EngineStatus, LlmEngine, Model, RemoteEngine } from '@janhq/core'
import {
EngineStatus,
LlmEngine,
LocalEngine,
Model,
RemoteEngine,
RemoteEngines,
} from '@janhq/core'
import { Button } from '@janhq/joi'
import { useSetAtom } from 'jotai'
import { SettingsIcon } from 'lucide-react'
import { useAtom, useSetAtom } from 'jotai'
import {
SettingsIcon,
ChevronDownIcon,
ChevronUpIcon,
PlusIcon,
} from 'lucide-react'
import { twMerge } from 'tailwind-merge'
import useEngineQuery from '@/hooks/useEngineQuery'
import useGetModelsByEngine from '@/hooks/useGetModelsByEngine'
import { getTitleByCategory } from '@/utils/model-engine'
import { getLogoByLocalEngine, getTitleByCategory } from '@/utils/model-engine'
import ModelLabel from '../ModelLabel'
import { showEngineListModelAtom } from '@/helpers/atoms/Model.atom'
import { setUpRemoteModelStageAtom } from '@/helpers/atoms/SetupRemoteModel.atom'
type Props = {
@ -35,6 +48,10 @@ const ModelSection: React.FC<Props> = ({
const setUpRemoteModelStage = useSetAtom(setUpRemoteModelStageAtom)
const { data: engineData } = useEngineQuery()
const [showEngineListModel, setShowEngineListModel] = useAtom(
showEngineListModelAtom
)
const engineLogo: string | undefined = models.find(
(entry) => entry?.metadata?.logo != null
)?.metadata?.logo
@ -50,18 +67,55 @@ const ModelSection: React.FC<Props> = ({
})
}, [apiKeyUrl, engine, engineLogo, setUpRemoteModelStage])
const isEngineReady =
engineData?.find((e) => e.name === engine)?.status === EngineStatus.Ready
const getEngineStatusReady: LlmEngine[] | undefined = engineData
?.filter((e) => e.status === EngineStatus.Ready)
.map((x) => x.name as LlmEngine)
const showModel = showEngineListModel.includes(engine)
const onClickChevron = useCallback(() => {
if (showModel) {
setShowEngineListModel((prev) => prev.filter((item) => item !== engine))
} else {
setShowEngineListModel((prev) => [...prev, engine])
}
}, [engine, setShowEngineListModel, showModel])
useEffect(() => {
const matchedModels = getModelsByEngine(engine, searchText)
setModels(matchedModels)
}, [getModelsByEngine, engine, searchText])
setShowEngineListModel((prev) => [
...prev,
...(getEngineStatusReady as LlmEngine[]),
])
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [getModelsByEngine, engine, searchText, setShowEngineListModel])
const engineName = getTitleByCategory(engine)
const localEngineLogo = getLogoByLocalEngine(engine as LocalEngine)
const isRemoteEngine = RemoteEngines.includes(engine as RemoteEngine)
if (models.length === 0) return null
const engineName = getTitleByCategory(engine)
return (
<div className="w-full pt-2">
<div className="w-full border-b border-[hsla(var(--app-border))] py-3 last:border-none">
<div className="flex justify-between pr-2">
<div className="flex gap-2 pl-3">
<div
className="flex cursor-pointer gap-2 pl-3"
onClick={onClickChevron}
>
{!isRemoteEngine && (
<Image
className="h-5 w-5 flex-shrink-0 rounded-full object-cover"
width={40}
height={40}
src={localEngineLogo as string}
alt="logo"
/>
)}
{engineLogo && (
<Image
className="h-5 w-5 flex-shrink-0 rounded-full object-cover"
@ -71,35 +125,58 @@ const ModelSection: React.FC<Props> = ({
alt="logo"
/>
)}
<h6 className="mb-1 pr-3 font-medium text-[hsla(var(--text-secondary))]">
<h6 className="pr-3 font-medium text-[hsla(var(--text-secondary))]">
{engineName}
</h6>
</div>
<Button theme="icon" onClick={onSettingClick}>
<SettingsIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
</Button>
<div className="flex items-center">
{isRemoteEngine && (
<Button theme="icon" onClick={onSettingClick}>
{isEngineReady ? (
<SettingsIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
) : (
<PlusIcon
size={14}
className="text-[hsla(var(--text-secondary))]"
/>
)}
</Button>
)}
{!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>
<ul className="pb-2">
<ul>
{models.map((model) => {
const isEngineReady =
engineData?.find((e) => e.name === model.engine)?.status ===
EngineStatus.Ready
if (!showModel) return null
return (
<li
key={model.model}
className={twMerge(
'flex cursor-pointer items-center gap-2 px-3 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]',
isEngineReady
!isRemoteEngine || isEngineReady
? 'text-[hsla(var(--text-primary))]'
: 'cursor-not-allowed text-[hsla(var(--text-tertiary))]'
: 'pointer-events-none cursor-not-allowed text-[hsla(var(--text-tertiary))]'
)}
onClick={() => {
if (isEngineReady) {
onModelSelected(model)
}
onModelSelected(model)
}}
>
<div className="flex w-full items-center justify-between">

View File

@ -1,4 +1,10 @@
import { ImportingModel, Model, ModelStatus } from '@janhq/core'
import {
ImportingModel,
LlmEngine,
LocalEngines,
Model,
ModelStatus,
} from '@janhq/core'
import { atom } from 'jotai'
import { activeThreadAtom, threadsAtom } from './Thread.atom'
@ -128,3 +134,5 @@ export const updateSelectedModelAtom = atom(null, (get, set, model: Model) => {
})
export const activeModelsAtom = atom<ModelStatus[]>([])
export const showEngineListModelAtom = atom<LlmEngine[]>([...LocalEngines])

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -74,7 +74,7 @@ const ThreadItem: React.FC<Props> = ({ thread }) => {
transition={{ ease: 'linear', duration: 0.2 }}
layoutId={thread.id}
className={twMerge(
'group/message relative mt-1 flex h-[28px] cursor-pointer items-center justify-between rounded-lg pl-2 first:mt-0 hover:bg-[hsla(var(--left-panel-menu-hover))]',
'group/message relative mr-1.5 mt-1 flex h-[28px] cursor-pointer items-center justify-between rounded-lg pl-2 first:mt-0 hover:bg-[hsla(var(--left-panel-menu-hover))]',
activeThreadId === thread.id &&
'text-primary bg-[hsla(var(--left-panel-icon-active-bg))] font-medium'
)}
@ -96,7 +96,7 @@ const ThreadItem: React.FC<Props> = ({ thread }) => {
</div>
<div
className={twMerge(
'absolute bottom-0 right-0 top-0 flex w-[42px] items-center justify-end rounded-r-md'
'absolute bottom-0 right-1.5 top-0 flex w-[42px] items-center justify-end rounded-r-md'
)}
>
<div

View File

@ -22,13 +22,12 @@ import { copyOverInstructionEnabledAtom } from '../ThreadRightPanel/AssistantSet
import ThreadItem from './ThreadItem'
import { getSelectedModelAtom } from '@/helpers/atoms/Model.atom'
import { reduceTransparentAtom } from '@/helpers/atoms/Setting.atom'
import { activeThreadAtom, threadsAtom } from '@/helpers/atoms/Thread.atom'
const ThreadLeftPanel: React.FC = () => {
const { setActiveThread } = useThreads()
const createThreadMutation = useThreadCreateMutation()
const reduceTransparent = useAtomValue(reduceTransparentAtom)
const selectedModel = useAtomValue(getSelectedModelAtom)
const threads = useAtomValue(threadsAtom)
@ -73,7 +72,7 @@ const ThreadLeftPanel: React.FC = () => {
return (
<LeftPanelContainer>
<div className={twMerge('pl-1.5 pt-3', reduceTransparent && 'pr-1.5')}>
<div className={twMerge('pl-1.5 pt-3')}>
<Button
className="mb-2"
data-testid="btn-create-thread"

View File

@ -1,8 +1,12 @@
import { LocalEngine } from '@janhq/core'
import { ModelHubCategory } from '@/hooks/useModelHub'
export const getTitleByCategory = (category: ModelHubCategory) => {
if (!category || !category.length) return ''
switch (category) {
case 'cortex.llamacpp':
return 'llama.cpp'
case 'BuiltInModels':
return 'Built-in Models'
case 'HuggingFace':
@ -37,3 +41,13 @@ export const getLogoByCategory = (category: ModelHubCategory) => {
return undefined
}
}
export const getLogoByLocalEngine = (engine: LocalEngine) => {
switch (engine) {
case 'cortex.llamacpp':
return 'icons/llamacpp.svg'
default:
return undefined
}
}