Faisal Amir faa09bd2bf
feat: Dekstop Revamp (#2877)
* feat: desktop revamp

* feat: refactor system monitor

* fix linter CI

* remove unused import component

* added responsive and resizeable component

* responsive and resizeable local server page

* finalize responsive and resizeable component

* fix scroll custom ui

* remove react scroll to bottom from modal troubleshoot

* fix modal troubleshoot ui

* fix setting gpu list

* text area custom scroll bar

* fix padding message input

* cleanup classname

* update inference engine model dropdown

* update loader style

* update quick ask ui

* prepare theme provider

* update dark theme

* remove update hotkey list model and navigation

* fix: cleanup hardcode classname

* fix: update feedback

* Set native theme electron

* update destop ui revamp from feedback

* update button icon component insider icon chat input message

* update model dropdown ui

* update tranaparent baclground

* update logo model provider

* fix: set background material acrylic support to blur background windows

* fix: update tranparent left and right panel

* fix: linter CI

* update app using frameless window

* styling custom style minimize, maximize and close app

* temporary hidden maximize window

* fix: responsive left and right panel

* fix: enable click outside when leftpanel responsive

* fix: remove unused import

* update transparent variable css windows

* fix: ui import model

* feat: Support Theme system (#2946)

* feat: update support theme system

* update select component

* feat: add theme folder in root project

* fix: padding left and right center panel

* fix: update padding left and right

* chore: migrate themes

* fix: rmdirsync error

* chore: update gitignore

* fix: cp recursive

* fix: files electron package json

* fix: migration

* fix: update fgit ignore

---------

Co-authored-by: Louis <louis@jan.ai>

* fix: update feedback missing state when refrash app

* fix: error test CI

* chore: refactor useLoadThemes

* chore: cleanup unused vars

* fix: revert back menubar windows

* fix minor ui

* fix: minor ui

---------

Co-authored-by: Louis <louis@jan.ai>
2024-05-29 13:37:18 +07:00

165 lines
5.1 KiB
TypeScript

import { useCallback, useEffect } from 'react'
import { Thread } from '@janhq/core'
import { Button } from '@janhq/joi'
import { motion as m } from 'framer-motion'
import { useAtomValue, useSetAtom } from 'jotai'
import {
GalleryHorizontalEndIcon,
MoreHorizontalIcon,
PenSquareIcon,
} from 'lucide-react'
import { twMerge } from 'tailwind-merge'
import LeftPanelContainer from '@/containers/LeftPanelContainer'
import { toaster } from '@/containers/Toast'
import { useCreateNewThread } from '@/hooks/useCreateNewThread'
import useRecommendedModel from '@/hooks/useRecommendedModel'
import useSetActiveThread from '@/hooks/useSetActiveThread'
import ModalCleanThread from './ModalCleanThread'
import ModalDeleteThread from './ModalDeleteThread'
import { assistantsAtom } from '@/helpers/atoms/Assistant.atom'
import { editMessageAtom } from '@/helpers/atoms/ChatMessage.atom'
import {
getActiveThreadIdAtom,
threadDataReadyAtom,
threadsAtom,
} from '@/helpers/atoms/Thread.atom'
const ThreadLeftPanel = () => {
const threads = useAtomValue(threadsAtom)
const activeThreadId = useAtomValue(getActiveThreadIdAtom)
const { setActiveThread } = useSetActiveThread()
const assistants = useAtomValue(assistantsAtom)
const threadDataReady = useAtomValue(threadDataReadyAtom)
const { requestCreateNewThread } = useCreateNewThread()
const setEditMessage = useSetAtom(editMessageAtom)
const { recommendedModel, downloadedModels } = useRecommendedModel()
const onThreadClick = useCallback(
(thread: Thread) => {
setActiveThread(thread)
setEditMessage('')
},
[setActiveThread, setEditMessage]
)
/**
* Auto create thread
* This will create a new thread if there are assistants available
* and there are no threads available
*/
useEffect(() => {
if (
threadDataReady &&
assistants.length > 0 &&
threads.length === 0 &&
(recommendedModel || downloadedModels[0])
) {
const model = recommendedModel || downloadedModels[0]
requestCreateNewThread(assistants[0], model)
} else if (threadDataReady && !activeThreadId) {
setActiveThread(threads[0])
}
}, [
assistants,
threads,
threadDataReady,
requestCreateNewThread,
activeThreadId,
setActiveThread,
recommendedModel,
downloadedModels,
])
const onCreateConversationClick = async () => {
if (assistants.length === 0) {
toaster({
title: 'No assistant available.',
description: `Could not create a new thread. Please add an assistant.`,
type: 'error',
})
} else {
requestCreateNewThread(assistants[0])
}
}
return (
<LeftPanelContainer>
{threads.length === 0 ? (
<div className="p-2 text-center">
<GalleryHorizontalEndIcon
size={16}
className="text-[hsla(var(--text-secondary)] mx-auto mb-3"
/>
<h2 className="font-medium">No Thread History</h2>
</div>
) : (
<div className="p-3">
<Button
className="mb-2"
data-testid="btn-create-thread"
onClick={onCreateConversationClick}
theme="icon"
>
<PenSquareIcon
size={16}
className="cursor-pointer text-[hsla(var(--text-secondary))]"
/>
</Button>
{threads.map((thread) => (
<div
key={thread.id}
className={twMerge(
`group/message relative mb-1 flex cursor-pointer flex-col transition-all hover:rounded-lg hover:bg-[hsla(var(--left-panel-menu-hover))]`
)}
onClick={() => {
onThreadClick(thread)
}}
>
<div className="relative z-10 p-2">
<h1
className={twMerge(
'line-clamp-1 pr-2 font-medium group-hover/message:pr-6',
activeThreadId && 'font-medium'
)}
>
{thread.title}
</h1>
</div>
<div
className={twMerge(
`group/icon text-[hsla(var(--text-secondary)] invisible absolute right-1 top-1/2 z-20 -translate-y-1/2 rounded-md px-0.5 group-hover/message:visible`
)}
>
<Button theme="icon" className="mt-2">
<MoreHorizontalIcon />
</Button>
<div className="invisible absolute -right-1 z-50 w-40 overflow-hidden rounded-lg border border-[hsla(var(--app-border))] bg-[hsla(var(--app-bg))] shadow-lg group-hover/icon:visible">
<ModalCleanThread threadId={thread.id} />
<ModalDeleteThread threadId={thread.id} />
</div>
</div>
{activeThreadId === thread.id && (
<m.div
className="absolute inset-0 left-0 h-full w-full rounded-lg bg-[hsla(var(--left-panel-icon-active-bg))]"
layoutId="active-thread"
/>
)}
</div>
))}
</div>
)}
</LeftPanelContainer>
)
}
export default ThreadLeftPanel