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:
parent
23cd5fd397
commit
8b44613015
@ -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 &&
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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])
|
||||
|
||||
15
web/public/icons/llamacpp.svg
Normal file
15
web/public/icons/llamacpp.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 43 KiB |
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user