From 66ee19babbacb01a3ea43f6c79f06aaaf89983a0 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Wed, 11 Sep 2024 17:29:46 +0800 Subject: [PATCH] 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. --- src/renderer/src/pages/home/Assistants.tsx | 54 ++++++++++++++++--- .../src/pages/home/Inputbar/Inputbar.tsx | 10 ++-- src/renderer/src/services/assistant.ts | 7 ++- src/renderer/src/store/runtime.ts | 9 +++- 4 files changed, 67 insertions(+), 13 deletions(-) diff --git a/src/renderer/src/pages/home/Assistants.tsx b/src/renderer/src/pages/home/Assistants.tsx index 788d0bad10..0dfe24680a 100644 --- a/src/renderer/src/pages/home/Assistants.tsx +++ b/src/renderer/src/pages/home/Assistants.tsx @@ -3,15 +3,17 @@ import DragableList from '@renderer/components/DragableList' import CopyIcon from '@renderer/components/Icons/CopyIcon' import AssistantSettingPopup from '@renderer/components/Popups/AssistantSettingPopup' import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant' +import { useRuntime } from '@renderer/hooks/useStore' import { getDefaultTopic, syncAsistantToAgent } from '@renderer/services/assistant' 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 { uuid } from '@renderer/utils' -import { Dropdown, Input } from 'antd' +import { Dropdown, Input, InputRef } from 'antd' import { ItemType } from 'antd/es/menu/interface' 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 styled from 'styled-components' @@ -26,7 +28,10 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs const generating = useAppSelector((state) => state.runtime.generating) const [search, setSearch] = useState('') const { updateAssistant, removeAllTopics } = useAssistant(activeAssistant.id) + const searchRef = useRef(null) const { t } = useTranslation() + const { searching } = useRuntime() + const dispatch = useAppDispatch() const onDelete = useCallback( (assistant: Assistant) => { @@ -108,14 +113,43 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs const list = assistants.filter((assistant) => assistant.name?.toLowerCase().includes(search.toLowerCase().trim())) const onSearch = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - if (list.length === 1) { - onSwitchAssistant(list[0]) - setSearch('') + const isEnterPressed = e.keyCode == 13 + + if (e.key === 'Escape') { + 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 ( {assistants.length >= 10 && ( @@ -129,6 +163,12 @@ const Assistants: FC = ({ activeAssistant, setActiveAssistant, onCreateAs onChange={(e) => setSearch(e.target.value)} style={{ borderRadius: 4 }} onKeyDown={onSearch} + ref={searchRef} + onFocus={() => dispatch(setSearching(true))} + onBlur={() => { + dispatch(setSearching(false)) + setSearch('') + }} allowClear /> diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index c772878825..1936f66aa1 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -10,12 +10,12 @@ import { } from '@ant-design/icons' import { useAssistant } from '@renderer/hooks/useAssistant' 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 { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { estimateInputTokenCount } from '@renderer/services/messages' -import store, { useAppSelector } from '@renderer/store' -import { setGenerating } from '@renderer/store/runtime' +import store, { useAppDispatch, useAppSelector } from '@renderer/store' +import { setGenerating, setSearching } from '@renderer/store/runtime' import { Assistant, Message, Topic } from '@renderer/types' import { delay, uuid } from '@renderer/utils' import { Button, Popconfirm, Tooltip } from 'antd' @@ -50,6 +50,8 @@ const Inputbar: FC = ({ assistant, setActiveTopic }) => { const { t } = useTranslation() const containerRef = useRef(null) const { showTopics, toggleShowTopics } = useShowTopics() + const { searching } = useRuntime() + const dispatch = useAppDispatch() _text = text @@ -215,6 +217,8 @@ const Inputbar: FC = ({ assistant, setActiveTopic }) => { onFocus={() => setInputFocus(true)} onBlur={() => setInputFocus(false)} onInput={onInput} + disabled={searching} + onClick={() => searching && dispatch(setSearching(false))} /> diff --git a/src/renderer/src/services/assistant.ts b/src/renderer/src/services/assistant.ts index 58e15191ab..46e115eb3d 100644 --- a/src/renderer/src/services/assistant.ts +++ b/src/renderer/src/services/assistant.ts @@ -15,6 +15,10 @@ export function getDefaultAssistant(): Assistant { } } +export function getDefaultAssistantSettings() { + return store.getState().assistants.defaultAssistant.settings +} + export function getDefaultTopic(): Topic { return { id: uuid(), @@ -84,8 +88,9 @@ export function covertAgentToAssistant(agent: Agent): Assistant { return { ...getDefaultAssistant(), ...agent, + id: agent.group === 'system' ? uuid() : String(agent.id), name: getAssistantNameWithAgent(agent), - id: agent.group === 'system' ? uuid() : String(agent.id) + settings: getDefaultAssistantSettings() } } diff --git a/src/renderer/src/store/runtime.ts b/src/renderer/src/store/runtime.ts index dc2b45cc2a..d42c72e850 100644 --- a/src/renderer/src/store/runtime.ts +++ b/src/renderer/src/store/runtime.ts @@ -5,12 +5,14 @@ export interface RuntimeState { avatar: string generating: boolean minappShow: boolean + searching: boolean } const initialState: RuntimeState = { avatar: UserAvatar, generating: false, - minappShow: false + minappShow: false, + searching: false } const runtimeSlice = createSlice({ @@ -25,10 +27,13 @@ const runtimeSlice = createSlice({ }, setMinappShow: (state, action: PayloadAction) => { state.minappShow = action.payload + }, + setSearching: (state, action: PayloadAction) => { + state.searching = action.payload } } }) -export const { setAvatar, setGenerating, setMinappShow } = runtimeSlice.actions +export const { setAvatar, setGenerating, setMinappShow, setSearching } = runtimeSlice.actions export default runtimeSlice.reducer