mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-06 21:35:52 +08:00
feat: Enhanced search functionality with user interaction and command shortcuts.
- Improved functionality to search Assistants with enhanced user interaction and command shortcuts. - Implemented search functionality with runtime state management. - Added functionality to return default assistant settings and updated conversion of agents to assistants to include default settings. - Added a new 'searching' boolean field and corresponding state update action to the runtime store.
This commit is contained in:
parent
42f0b5f8fc
commit
dd3c81ec5f
@ -3,15 +3,17 @@ import DragableList from '@renderer/components/DragableList'
|
|||||||
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
import CopyIcon from '@renderer/components/Icons/CopyIcon'
|
||||||
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
|
import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup'
|
||||||
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
|
import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
|
import { useRuntime } from '@renderer/hooks/useStore'
|
||||||
import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant'
|
import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
|
import { setSearching } from '@renderer/store/runtime'
|
||||||
import { Assistant } from '@renderer/types'
|
import { Assistant } from '@renderer/types'
|
||||||
import { uuid } from '@renderer/utils'
|
import { uuid } from '@renderer/utils'
|
||||||
import { Dropdown, Input } from 'antd'
|
import { Dropdown, Input, InputRef } from 'antd'
|
||||||
import { ItemType } from 'antd/es/menu/interface'
|
import { ItemType } from 'antd/es/menu/interface'
|
||||||
import { isEmpty, last } from 'lodash'
|
import { isEmpty, last } from 'lodash'
|
||||||
import { FC, useCallback, useState } from 'react'
|
import { FC, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
@ -26,7 +28,10 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
const generating = useAppSelector((state) => state.runtime.generating)
|
const generating = useAppSelector((state) => state.runtime.generating)
|
||||||
const [search, setSearch] = useState('')
|
const [search, setSearch] = useState('')
|
||||||
const { updateAssistant, removeAllTopics } = useAssistant(activeAssistant.id)
|
const { updateAssistant, removeAllTopics } = useAssistant(activeAssistant.id)
|
||||||
|
const searchRef = useRef<InputRef>(null)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { searching } = useRuntime()
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
const onDelete = useCallback(
|
const onDelete = useCallback(
|
||||||
(assistant: Assistant) => {
|
(assistant: Assistant) => {
|
||||||
@ -108,14 +113,43 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
const list = assistants.filter((assistant) => assistant.name?.toLowerCase().includes(search.toLowerCase().trim()))
|
const list = assistants.filter((assistant) => assistant.name?.toLowerCase().includes(search.toLowerCase().trim()))
|
||||||
|
|
||||||
const onSearch = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const onSearch = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (e.key === 'Enter') {
|
const isEnterPressed = e.keyCode == 13
|
||||||
if (list.length === 1) {
|
|
||||||
onSwitchAssistant(list[0])
|
if (e.key === 'Escape') {
|
||||||
setSearch('')
|
return searchRef.current?.blur()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEnterPressed) {
|
||||||
|
if (list.length > 0) {
|
||||||
|
if (list.length === 1) {
|
||||||
|
onSwitchAssistant(list[0])
|
||||||
|
setSearch('')
|
||||||
|
setTimeout(() => searchRef.current?.blur(), 0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const index = list.findIndex((a) => a.id === activeAssistant?.id)
|
||||||
|
onSwitchAssistant(index === list.length - 1 ? list[0] : list[index + 1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||||
|
searchRef.current?.focus()
|
||||||
|
searchRef.current?.select()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command or Ctrl + K create new topic
|
||||||
|
useEffect(() => {
|
||||||
|
const onKeydown = (e) => {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
||||||
|
searchRef.current?.focus()
|
||||||
|
searchRef.current?.select()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('keydown', onKeydown)
|
||||||
|
return () => document.removeEventListener('keydown', onKeydown)
|
||||||
|
}, [activeAssistant?.id, list, onSwitchAssistant])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
{assistants.length >= 10 && (
|
{assistants.length >= 10 && (
|
||||||
@ -129,6 +163,12 @@ const Assistants: FC<Props> = ({ activeAssistant, setActiveAssistant, onCreateAs
|
|||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
style={{ borderRadius: 4 }}
|
style={{ borderRadius: 4 }}
|
||||||
onKeyDown={onSearch}
|
onKeyDown={onSearch}
|
||||||
|
ref={searchRef}
|
||||||
|
onFocus={() => dispatch(setSearching(true))}
|
||||||
|
onBlur={() => {
|
||||||
|
dispatch(setSearching(false))
|
||||||
|
setSearch('')
|
||||||
|
}}
|
||||||
allowClear
|
allowClear
|
||||||
/>
|
/>
|
||||||
</SearchContainer>
|
</SearchContainer>
|
||||||
|
|||||||
@ -10,12 +10,12 @@ import {
|
|||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useShowTopics } from '@renderer/hooks/useStore'
|
import { useRuntime, useShowTopics } from '@renderer/hooks/useStore'
|
||||||
import { getDefaultTopic } from '@renderer/services/assistant'
|
import { getDefaultTopic } from '@renderer/services/assistant'
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { estimateInputTokenCount } from '@renderer/services/messages'
|
import { estimateInputTokenCount } from '@renderer/services/messages'
|
||||||
import store, { useAppSelector } from '@renderer/store'
|
import store, { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import { setGenerating } from '@renderer/store/runtime'
|
import { setGenerating, setSearching } from '@renderer/store/runtime'
|
||||||
import { Assistant, Message, Topic } from '@renderer/types'
|
import { Assistant, Message, Topic } from '@renderer/types'
|
||||||
import { delay, uuid } from '@renderer/utils'
|
import { delay, uuid } from '@renderer/utils'
|
||||||
import { Button, Popconfirm, Tooltip } from 'antd'
|
import { Button, Popconfirm, Tooltip } from 'antd'
|
||||||
@ -50,6 +50,8 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const containerRef = useRef(null)
|
const containerRef = useRef(null)
|
||||||
const { showTopics, toggleShowTopics } = useShowTopics()
|
const { showTopics, toggleShowTopics } = useShowTopics()
|
||||||
|
const { searching } = useRuntime()
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
_text = text
|
_text = text
|
||||||
|
|
||||||
@ -215,6 +217,8 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
|||||||
onFocus={() => setInputFocus(true)}
|
onFocus={() => setInputFocus(true)}
|
||||||
onBlur={() => setInputFocus(false)}
|
onBlur={() => setInputFocus(false)}
|
||||||
onInput={onInput}
|
onInput={onInput}
|
||||||
|
disabled={searching}
|
||||||
|
onClick={() => searching && dispatch(setSearching(false))}
|
||||||
/>
|
/>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<ToolbarMenu>
|
<ToolbarMenu>
|
||||||
|
|||||||
@ -15,6 +15,10 @@ export function getDefaultAssistant(): Assistant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDefaultAssistantSettings() {
|
||||||
|
return store.getState().assistants.defaultAssistant.settings
|
||||||
|
}
|
||||||
|
|
||||||
export function getDefaultTopic(): Topic {
|
export function getDefaultTopic(): Topic {
|
||||||
return {
|
return {
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
@ -84,8 +88,9 @@ export function covertAgentToAssistant(agent: Agent): Assistant {
|
|||||||
return {
|
return {
|
||||||
...getDefaultAssistant(),
|
...getDefaultAssistant(),
|
||||||
...agent,
|
...agent,
|
||||||
|
id: agent.group === 'system' ? uuid() : String(agent.id),
|
||||||
name: getAssistantNameWithAgent(agent),
|
name: getAssistantNameWithAgent(agent),
|
||||||
id: agent.group === 'system' ? uuid() : String(agent.id)
|
settings: getDefaultAssistantSettings()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,12 +5,14 @@ export interface RuntimeState {
|
|||||||
avatar: string
|
avatar: string
|
||||||
generating: boolean
|
generating: boolean
|
||||||
minappShow: boolean
|
minappShow: boolean
|
||||||
|
searching: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: RuntimeState = {
|
const initialState: RuntimeState = {
|
||||||
avatar: UserAvatar,
|
avatar: UserAvatar,
|
||||||
generating: false,
|
generating: false,
|
||||||
minappShow: false
|
minappShow: false,
|
||||||
|
searching: false
|
||||||
}
|
}
|
||||||
|
|
||||||
const runtimeSlice = createSlice({
|
const runtimeSlice = createSlice({
|
||||||
@ -25,10 +27,13 @@ const runtimeSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setMinappShow: (state, action: PayloadAction<boolean>) => {
|
setMinappShow: (state, action: PayloadAction<boolean>) => {
|
||||||
state.minappShow = action.payload
|
state.minappShow = action.payload
|
||||||
|
},
|
||||||
|
setSearching: (state, action: PayloadAction<boolean>) => {
|
||||||
|
state.searching = action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const { setAvatar, setGenerating, setMinappShow } = runtimeSlice.actions
|
export const { setAvatar, setGenerating, setMinappShow, setSearching } = runtimeSlice.actions
|
||||||
|
|
||||||
export default runtimeSlice.reducer
|
export default runtimeSlice.reducer
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user