fix: handle long thread title without space (#3107)
* fix: handle long thread title without space, and make searchbar autofocus inside model dropdown * feat: enable right click to show setting on thread items (#3108)
This commit is contained in:
parent
e77f651273
commit
a2a203b40d
@ -1,4 +1,4 @@
|
||||
import { useState, useMemo, useEffect, useCallback } from 'react'
|
||||
import { useState, useMemo, useEffect, useCallback, useRef } from 'react'
|
||||
|
||||
import { InferenceEngine } from '@janhq/core'
|
||||
import { Badge, Input, ScrollArea, Select, useClickOutside } from '@janhq/joi'
|
||||
@ -70,7 +70,7 @@ const ModelDropdown = ({
|
||||
const downloadStates = useAtomValue(modelDownloadStateAtom)
|
||||
const setThreadModelParams = useSetAtom(setThreadModelParamsAtom)
|
||||
const { updateModelParameter } = useUpdateModelParameters()
|
||||
|
||||
const searchInputRef = useRef<HTMLInputElement>(null)
|
||||
const configuredModels = useAtomValue(configuredModelsAtom)
|
||||
const featuredModel = configuredModels.filter((x) =>
|
||||
x.metadata.tags.includes('Featured')
|
||||
@ -108,6 +108,12 @@ const ModelDropdown = ({
|
||||
[configuredModels, searchText, searchFilter]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (open && searchInputRef.current) {
|
||||
searchInputRef.current.focus()
|
||||
}
|
||||
}, [open])
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeThread) return
|
||||
let model = downloadedModels.find(
|
||||
@ -258,6 +264,7 @@ const ModelDropdown = ({
|
||||
<Input
|
||||
placeholder="Search"
|
||||
value={searchText}
|
||||
ref={searchInputRef}
|
||||
className="rounded-none border-x-0 border-t-0 focus-within:ring-0 hover:border-b-[hsla(var(--app-border))]"
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
suffixIcon={
|
||||
|
||||
@ -7,9 +7,10 @@ import useDeleteThread from '@/hooks/useDeleteThread'
|
||||
|
||||
type Props = {
|
||||
threadId: string
|
||||
closeContextMenu?: () => void
|
||||
}
|
||||
|
||||
const ModalCleanThread = ({ threadId }: Props) => {
|
||||
const ModalCleanThread = ({ threadId, closeContextMenu }: Props) => {
|
||||
const { cleanThread } = useDeleteThread()
|
||||
const onCleanThreadClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
@ -22,6 +23,11 @@ const ModalCleanThread = ({ threadId }: Props) => {
|
||||
return (
|
||||
<Modal
|
||||
title="Clean Thread"
|
||||
onOpenChange={(open) => {
|
||||
if (open && closeContextMenu) {
|
||||
closeContextMenu()
|
||||
}
|
||||
}}
|
||||
trigger={
|
||||
<div
|
||||
className="flex cursor-pointer items-center space-x-2 px-4 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]"
|
||||
|
||||
@ -7,10 +7,12 @@ import useDeleteThread from '@/hooks/useDeleteThread'
|
||||
|
||||
type Props = {
|
||||
threadId: string
|
||||
closeContextMenu?: () => void
|
||||
}
|
||||
|
||||
const ModalDeleteThread = ({ threadId }: Props) => {
|
||||
const ModalDeleteThread = ({ threadId, closeContextMenu }: Props) => {
|
||||
const { deleteThread } = useDeleteThread()
|
||||
|
||||
const onDeleteThreadClick = useCallback(
|
||||
(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
e.stopPropagation()
|
||||
@ -22,6 +24,11 @@ const ModalDeleteThread = ({ threadId }: Props) => {
|
||||
return (
|
||||
<Modal
|
||||
title="Delete Thread"
|
||||
onOpenChange={(open) => {
|
||||
if (open && closeContextMenu) {
|
||||
closeContextMenu()
|
||||
}
|
||||
}}
|
||||
trigger={
|
||||
<div
|
||||
className="flex cursor-pointer items-center space-x-2 px-4 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]"
|
||||
|
||||
@ -8,9 +8,10 @@ import { useCreateNewThread } from '@/hooks/useCreateNewThread'
|
||||
|
||||
type Props = {
|
||||
thread: Thread
|
||||
closeContextMenu?: () => void
|
||||
}
|
||||
|
||||
const ModalEditTitleThread = ({ thread }: Props) => {
|
||||
const ModalEditTitleThread = ({ thread, closeContextMenu }: Props) => {
|
||||
const [title, setTitle] = useState(thread.title)
|
||||
|
||||
const { updateThreadMetadata } = useCreateNewThread()
|
||||
@ -30,6 +31,11 @@ const ModalEditTitleThread = ({ thread }: Props) => {
|
||||
return (
|
||||
<Modal
|
||||
title="Edit title thread"
|
||||
onOpenChange={(open) => {
|
||||
if (open && closeContextMenu) {
|
||||
closeContextMenu()
|
||||
}
|
||||
}}
|
||||
trigger={
|
||||
<div
|
||||
className="flex cursor-pointer items-center space-x-2 px-4 py-2 hover:bg-[hsla(var(--dropdown-menu-hover-bg))]"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import { Thread } from '@janhq/core'
|
||||
|
||||
@ -43,6 +43,14 @@ const ThreadLeftPanel = () => {
|
||||
const setEditMessage = useSetAtom(editMessageAtom)
|
||||
const { recommendedModel, downloadedModels } = useRecommendedModel()
|
||||
|
||||
const [contextMenu, setContextMenu] = useState<{
|
||||
visible: boolean
|
||||
thread?: Thread
|
||||
}>({
|
||||
visible: false,
|
||||
thread: undefined,
|
||||
})
|
||||
|
||||
const onThreadClick = useCallback(
|
||||
(thread: Thread) => {
|
||||
setActiveThread(thread)
|
||||
@ -91,6 +99,21 @@ const ThreadLeftPanel = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const onContextMenu = (event: React.MouseEvent, thread: Thread) => {
|
||||
event.preventDefault()
|
||||
setContextMenu({
|
||||
visible: true,
|
||||
thread,
|
||||
})
|
||||
}
|
||||
|
||||
const closeContextMenu = () => {
|
||||
setContextMenu({
|
||||
visible: false,
|
||||
thread: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<LeftPanelContainer>
|
||||
{threads.length === 0 ? (
|
||||
@ -124,8 +147,10 @@ const ThreadLeftPanel = () => {
|
||||
onClick={() => {
|
||||
onThreadClick(thread)
|
||||
}}
|
||||
onContextMenu={(e) => onContextMenu(e, thread)}
|
||||
onMouseLeave={closeContextMenu}
|
||||
>
|
||||
<div className="relative z-10 p-2">
|
||||
<div className="relative z-10 break-all p-2">
|
||||
<h1
|
||||
className={twMerge(
|
||||
'line-clamp-1 pr-2 font-medium group-hover/message:pr-6',
|
||||
@ -143,10 +168,26 @@ const ThreadLeftPanel = () => {
|
||||
<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">
|
||||
<ModalEditTitleThread thread={thread} />
|
||||
<ModalCleanThread threadId={thread.id} />
|
||||
<ModalDeleteThread threadId={thread.id} />
|
||||
<div
|
||||
className={twMerge(
|
||||
'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',
|
||||
contextMenu.visible &&
|
||||
contextMenu.thread?.id === thread.id &&
|
||||
'visible'
|
||||
)}
|
||||
>
|
||||
<ModalEditTitleThread
|
||||
thread={thread}
|
||||
closeContextMenu={closeContextMenu}
|
||||
/>
|
||||
<ModalCleanThread
|
||||
threadId={thread.id}
|
||||
closeContextMenu={closeContextMenu}
|
||||
/>
|
||||
<ModalDeleteThread
|
||||
threadId={thread.id}
|
||||
closeContextMenu={closeContextMenu}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{activeThreadId === thread.id && (
|
||||
|
||||
@ -166,7 +166,7 @@ const Tools = () => {
|
||||
<div className="mb-2 flex items-center">
|
||||
<label
|
||||
id="vector-database"
|
||||
className="inline-block font-medium"
|
||||
className="inline-flex items-center font-medium"
|
||||
>
|
||||
Vector Database
|
||||
<Tooltip
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user