From bb1c39dd52d1195570e9325e6224c87037a04370 Mon Sep 17 00:00:00 2001 From: icinggslits <93433084+icinggslits@users.noreply.github.com> Date: Tue, 25 Feb 2025 19:54:31 +0800 Subject: [PATCH 1/2] feature: Add switch navigation shortcut (#2119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature: Add switch navigation shortcut # Conflicts: # src/renderer/src/i18n/locales/en-us.json # src/renderer/src/i18n/locales/ja-jp.json # src/renderer/src/i18n/locales/ru-ru.json # src/renderer/src/i18n/locales/zh-tw.json # src/renderer/src/pages/translate/TranslatePage.tsx # Conflicts: # src/renderer/src/store/migrate.ts * 复原 * 尝试提交 * 尝试回滚 * feat: Add switch navigation shortcut --- src/renderer/src/components/app/Sidebar.tsx | 20 +++-- .../src/handler/NavigationHandler.tsx | 89 ++++++++++++++++++- src/renderer/src/i18n/locales/en-us.json | 8 +- src/renderer/src/i18n/locales/ja-jp.json | 8 +- src/renderer/src/i18n/locales/ru-ru.json | 8 +- src/renderer/src/i18n/locales/zh-cn.json | 8 +- src/renderer/src/i18n/locales/zh-tw.json | 8 +- src/renderer/src/pages/agents/AgentsPage.tsx | 48 +++++++++- src/renderer/src/pages/apps/AppsPage.tsx | 1 + src/renderer/src/pages/files/FilesPage.tsx | 32 ++++++- .../src/pages/home/Tabs/AssistantsTab.tsx | 26 +++++- .../src/pages/home/Tabs/TopicsTab.tsx | 15 ++++ src/renderer/src/pages/home/Tabs/index.tsx | 49 ++++++---- .../src/pages/knowledge/KnowledgePage.tsx | 19 ++++ .../src/pages/settings/SettingsPage.tsx | 26 +++++- .../src/pages/translate/TranslatePage.tsx | 43 +++------ src/renderer/src/store/migrate.ts | 47 ++++++++++ src/renderer/src/store/shortcuts.ts | 42 +++++++++ 18 files changed, 430 insertions(+), 67 deletions(-) diff --git a/src/renderer/src/components/app/Sidebar.tsx b/src/renderer/src/components/app/Sidebar.tsx index 053913c202..8c288ca4e0 100644 --- a/src/renderer/src/components/app/Sidebar.tsx +++ b/src/renderer/src/components/app/Sidebar.tsx @@ -26,6 +26,16 @@ import MinAppIcon from '../Icons/MinAppIcon' import MinApp from '../MinApp' import UserPopup from '../Popups/UserPopup' +export const locationPathnameMappingPathMap = { + assistants: '/', + agents: '/agents', + paintings: '/paintings', + translate: '/translate', + minapp: '/apps', + knowledge: '/knowledge', + files: '/files' +} + const Sidebar: FC = () => { const { pathname } = useLocation() const avatar = useAvatar() @@ -133,15 +143,7 @@ const MainMenus: FC = () => { files: } - const pathMap = { - assistants: '/', - agents: '/agents', - paintings: '/paintings', - translate: '/translate', - minapp: '/apps', - knowledge: '/knowledge', - files: '/files' - } + const pathMap = locationPathnameMappingPathMap return sidebarIcons.visible.map((icon) => { const path = pathMap[icon] diff --git a/src/renderer/src/handler/NavigationHandler.tsx b/src/renderer/src/handler/NavigationHandler.tsx index c780e467c3..f5acbf4191 100644 --- a/src/renderer/src/handler/NavigationHandler.tsx +++ b/src/renderer/src/handler/NavigationHandler.tsx @@ -1,8 +1,16 @@ +import { locationPathnameMappingPathMap } from '@renderer/components/app/Sidebar' +import { useSettings } from '@renderer/hooks/useSettings' +import { useShortcut } from '@renderer/hooks/useShortcuts' +import { settingMenuItemPathList } from '@renderer/pages/settings/SettingsPage' +import Logger from 'electron-log' +import React from 'react' import { useHotkeys } from 'react-hotkeys-hook' -import { useNavigate } from 'react-router-dom' +import { useLocation, useNavigate } from 'react-router-dom' const NavigationHandler: React.FC = () => { const navigate = useNavigate() + const { sidebarIcons } = useSettings() + const { pathname: navigationPathname } = useLocation() useHotkeys( 'meta+, ! ctrl+,', function () { @@ -11,6 +19,85 @@ const NavigationHandler: React.FC = () => { { splitKey: '!' } ) + const navigationListAndIndex = (): { + err: boolean + errorMessage: string + navigationList: string[] + index: number + defaultSettingSubPathname?: string + } => { + const pinnedPathname = sidebarIcons.visible.map((iconName) => locationPathnameMappingPathMap[iconName]) + if (pinnedPathname.length !== sidebarIcons.visible.length) { + return { + err: true, + errorMessage: '[NavigationHandler] pinnedPathname length not match', + navigationList: [], + index: 0 + } + } else { + const navigationList = [...pinnedPathname, '/settings'] + const currentPathname = (() => { + const path = navigationPathname.split('/')[1] + if (path === '') { + return '/' + } else { + return `/${path}` + } + })() + + const index = navigationList.findIndex((iconName) => iconName === currentPathname) + if (index === -1) { + return { + err: true, + errorMessage: '[NavigationHandler] currentPathname not found', + navigationList, + index: 0 + } + } else { + return { + err: false, + errorMessage: '', + navigationList, + index + } + } + } + } + + const gotoNavigateByIndex = (navigationList: string[], index: number) => { + const listLength = navigationList.length + let i = index % listLength + if (i < 0) { + i = (i + listLength) % listLength + } + const targetPathname = navigationList[i] + if (targetPathname.startsWith('/settings')) { + navigate(settingMenuItemPathList?.[0] ?? '/settings/provider') + } else { + navigate(targetPathname) + } + } + + useShortcut('switch_to_prev_main_navigation', () => { + const result = navigationListAndIndex() + if (result.err) { + Logger.error(result.errorMessage) + } else { + const { navigationList, index } = result + gotoNavigateByIndex(navigationList, index - 1) + } + }) + + useShortcut('switch_to_next_main_navigation', () => { + const result = navigationListAndIndex() + if (result.err) { + Logger.error(result.errorMessage) + } else { + const { navigationList, index } = result + gotoNavigateByIndex(navigationList, index + 1) + } + }) + return null } diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index e9985955f6..3058509c6d 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -805,7 +805,13 @@ "toggle_show_topics": "Toggle Topics", "zoom_in": "Zoom In", "zoom_out": "Zoom Out", - "zoom_reset": "Reset Zoom" + "zoom_reset": "Reset Zoom", + "switch_to_prev_main_navigation": "Switch to previous main navigation", + "switch_to_next_main_navigation": "Switch to next main navigation", + "switch_to_prev_main_tab": "Switch to previous main tab", + "switch_to_next_main_tab": "Switch to next main tab", + "switch_to_prev_horizontal_tab": "Switch to previous horizontal tab", + "switch_to_next_horizontal_tab": "Switch to next horizontal tab" }, "theme.auto": "Auto", "theme.dark": "Dark", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 6419b8045d..d93ab55d34 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -804,7 +804,13 @@ "toggle_show_topics": "トピックの表示を切り替え", "zoom_in": "ズームイン", "zoom_out": "ズームアウト", - "zoom_reset": "ズームをリセット" + "zoom_reset": "ズームをリセット", + "switch_to_prev_main_navigation": "前のメインナビゲーションに切り替え", + "switch_to_next_main_navigation": "次のメインナビゲーションに切り替え", + "switch_to_prev_main_tab": "前のメインタブに切り替え", + "switch_to_next_main_tab": "次のメインタブに切り替え", + "switch_to_prev_horizontal_tab": "前の水平タブに切り替え", + "switch_to_next_horizontal_tab": "次の水平タブに切り替え" }, "theme.auto": "自動", "theme.dark": "ダークテーマ", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 0389ab8114..cc25e10890 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -804,7 +804,13 @@ "toggle_show_topics": "Переключить отображение топиков", "zoom_in": "Увеличить", "zoom_out": "Уменьшить", - "zoom_reset": "Сбросить масштаб" + "zoom_reset": "Сбросить масштаб", + "switch_to_prev_main_navigation": "Переключиться к предыдущей главной навигации", + "switch_to_next_main_navigation": "Переключиться к следующей главной навигации", + "switch_to_prev_main_tab": "Переключиться к предыдущей главной вкладке", + "switch_to_next_main_tab": "Переключиться к следующей главной вкладке", + "switch_to_prev_horizontal_tab": "Переключиться к предыдущей горизонтальной вкладке", + "switch_to_next_horizontal_tab": "Переключиться к следующей горизонтальной вкладке" }, "theme.auto": "Автоматически", "theme.dark": "Темная", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 7afb89d9a4..b7b5bbbe87 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -804,7 +804,13 @@ "toggle_show_topics": "切换话题显示", "zoom_in": "放大界面", "zoom_out": "缩小界面", - "zoom_reset": "重置缩放" + "zoom_reset": "重置缩放", + "switch_to_prev_main_navigation": "导航栏向上切换", + "switch_to_next_main_navigation": "导航栏向下切换", + "switch_to_prev_main_tab": "侧边栏向上切换", + "switch_to_next_main_tab": "侧边栏向下切换", + "switch_to_prev_horizontal_tab": "选项卡向左切换", + "switch_to_next_horizontal_tab": "选项卡向右切换" }, "theme.auto": "跟随系统", "theme.dark": "深色主题", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 50b733c7f2..8fc1991d50 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -804,7 +804,13 @@ "toggle_show_topics": "切換話題顯示", "zoom_in": "放大界面", "zoom_out": "縮小界面", - "zoom_reset": "重置縮放" + "zoom_reset": "重置縮放", + "switch_to_prev_main_navigation": "導航欄向上切換", + "switch_to_next_main_navigation": "導航欄向下切換", + "switch_to_prev_main_tab": "側邊欄向上切換", + "switch_to_next_main_tab": "側邊欄向下切換", + "switch_to_prev_horizontal_tab": "選項卡向左切換", + "switch_to_next_horizontal_tab": "選項卡向右切換" }, "theme.auto": "自動", "theme.dark": "深色主題", diff --git a/src/renderer/src/pages/agents/AgentsPage.tsx b/src/renderer/src/pages/agents/AgentsPage.tsx index a8a30bba8f..922305956a 100644 --- a/src/renderer/src/pages/agents/AgentsPage.tsx +++ b/src/renderer/src/pages/agents/AgentsPage.tsx @@ -1,6 +1,7 @@ import { SearchOutlined } from '@ant-design/icons' import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar' import Scrollbar from '@renderer/components/Scrollbar' +import { useShortcut } from '@renderer/hooks/useShortcuts' import { createAssistantFromAgent } from '@renderer/services/AssistantService' import { Agent } from '@renderer/types' import { uuid } from '@renderer/utils' @@ -24,6 +25,7 @@ const AgentsPage: FC = () => { const [search, setSearch] = useState('') const [searchInput, setSearchInput] = useState('') const systemAgents = useSystemAgents() + const [tabActiveKey, setTabActiveKey] = useState('1') const agentGroups = useMemo(() => { if (Object.keys(_agentGroups).length === 0) { @@ -159,6 +161,24 @@ const AgentsPage: FC = () => { } } + useShortcut('switch_to_prev_main_tab', () => { + if (tabItems.length > 1) { + const index = tabItems.findIndex((item) => item.key === tabActiveKey) + if (index !== -1) { + setTabActiveKey(tabItems[index === 0 ? tabItems.length - 1 : index - 1].key) + } + } + }) + + useShortcut('switch_to_next_main_tab', () => { + if (tabItems.length > 1) { + const index = tabItems.findIndex((item) => item.key === tabActiveKey) + if (index !== -1) { + setTabActiveKey(tabItems[index === tabItems.length - 1 ? 0 : index + 1].key) + } + } + }) + return ( @@ -177,6 +197,7 @@ const AgentsPage: FC = () => { maxLength={50} onChange={(e) => setSearchInput(e.target.value)} onPressEnter={handleSearch} + autoFocus />
@@ -187,7 +208,14 @@ const AgentsPage: FC = () => { search.trim() ? ( {renderAgentList(Object.values(filteredAgentGroups).flat())} ) : ( - + setTabActiveKey(key)} + /> ) ) : ( @@ -251,7 +279,11 @@ const EmptyView = styled.div` color: var(--color-text-secondary); ` -const Tabs = styled(TabsAntd)<{ $language: string }>` +const Tabs = styled(TabsAntd)<{ + $language: string + activeKey: string + onTabClick: (key: string, event: MouseEvent) => void +}>` display: flex; flex: 1; flex-direction: row-reverse; @@ -259,18 +291,22 @@ const Tabs = styled(TabsAntd)<{ $language: string }>` .ant-tabs-tabpane { padding-right: 0 !important; } + .ant-tabs-nav { min-width: ${({ $language }) => ($language.startsWith('zh') ? '120px' : '140px')}; max-width: ${({ $language }) => ($language.startsWith('zh') ? '120px' : '140px')}; position: relative; overflow: hidden; } + .ant-tabs-nav-list { padding: 10px 8px; } + .ant-tabs-nav-operations { display: none !important; } + .ant-tabs-tab { margin: 0 !important; border-radius: var(--list-item-border-radius); @@ -283,6 +319,7 @@ const Tabs = styled(TabsAntd)<{ $language: string }>` user-select: none; transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); outline: none !important; + .ant-tabs-tab-btn { white-space: nowrap; overflow: hidden; @@ -291,31 +328,38 @@ const Tabs = styled(TabsAntd)<{ $language: string }>` transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); outline: none !important; } + &:hover { color: var(--color-text) !important; background-color: var(--color-background-soft); } } + .ant-tabs-tab-active { background-color: var(--color-background-soft); border: 0.5px solid var(--color-border); transform: scale(1.02); } + .ant-tabs-content-holder { border-left: 0.5px solid var(--color-border); border-right: none; } + .ant-tabs-ink-bar { display: none; } + .ant-tabs-tab-btn:active { color: var(--color-text) !important; } + .ant-tabs-tab-active { .ant-tabs-tab-btn { color: var(--color-text) !important; } } + .ant-tabs-content { transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); } diff --git a/src/renderer/src/pages/apps/AppsPage.tsx b/src/renderer/src/pages/apps/AppsPage.tsx index 912ca371b0..ece3f17750 100644 --- a/src/renderer/src/pages/apps/AppsPage.tsx +++ b/src/renderer/src/pages/apps/AppsPage.tsx @@ -46,6 +46,7 @@ const AppsPage: FC = () => { suffix={} value={search} onChange={(e) => setSearch(e.target.value)} + autoFocus />
diff --git a/src/renderer/src/pages/files/FilesPage.tsx b/src/renderer/src/pages/files/FilesPage.tsx index a248ef6486..68240f0a13 100644 --- a/src/renderer/src/pages/files/FilesPage.tsx +++ b/src/renderer/src/pages/files/FilesPage.tsx @@ -11,6 +11,7 @@ import TextEditPopup from '@renderer/components/Popups/TextEditPopup' import Scrollbar from '@renderer/components/Scrollbar' import db from '@renderer/databases' import { useProviders } from '@renderer/hooks/useProvider' +import { useShortcut } from '@renderer/hooks/useShortcuts' import FileManager from '@renderer/services/FileManager' import store from '@renderer/store' import { FileType, FileTypes } from '@renderer/types' @@ -19,12 +20,19 @@ import type { MenuProps } from 'antd' import { Button, Dropdown, Menu } from 'antd' import dayjs from 'dayjs' import { useLiveQuery } from 'dexie-react-hooks' -import { FC, useMemo, useState } from 'react' +import React, { FC, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' import ContentView from './ContentView' +interface MenuItem { + key: string + label: string + icon: React.ReactNode + onClick?: () => void +} + const FilesPage: FC = () => { const { t } = useTranslation() const [fileType, setFileType] = useState('all') @@ -42,7 +50,7 @@ const FilesPage: FC = () => { const handleDelete = async (fileId: string) => { const file = await FileManager.getFile(fileId) - const paintings = await store.getState().paintings.paintings + const paintings = store.getState().paintings.paintings const paintingsFiles = paintings.flatMap((p) => p.files) if (paintingsFiles.some((p) => p.id === fileId)) { @@ -180,6 +188,26 @@ const FilesPage: FC = () => { })) ].filter(Boolean) as MenuProps['items'] + useShortcut('switch_to_prev_main_tab', () => { + const items = menuItems as MenuItem[] + if (items.length > 1) { + const index = items.findIndex((item) => item.key === fileType) + if (index !== -1) { + setFileType(items[index === 0 ? items.length - 1 : index - 1].key as FileTypes) + } + } + }) + + useShortcut('switch_to_next_main_tab', () => { + const items = menuItems as MenuItem[] + if (items.length > 1) { + const index = items.findIndex((item) => item.key === fileType) + if (index !== -1) { + setFileType(items[index === items.length - 1 ? 0 : index + 1].key as FileTypes) + } + } + }) + return ( diff --git a/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx b/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx index 93f9480421..be0fedc709 100644 --- a/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx @@ -2,7 +2,13 @@ import { PlusOutlined } from '@ant-design/icons' import DragableList from '@renderer/components/DragableList' import Scrollbar from '@renderer/components/Scrollbar' import { useAgents } from '@renderer/hooks/useAgents' -import { useAssistants } from '@renderer/hooks/useAssistant' +import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant' +import { modelGenerating } from '@renderer/hooks/useRuntime' +import { useSettings } from '@renderer/hooks/useSettings' +import { useShortcut } from '@renderer/hooks/useShortcuts' +import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings' +import { getDefaultTopic } from '@renderer/services/AssistantService' +import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { Assistant } from '@renderer/types' import { FC, useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -38,6 +44,24 @@ const Assistants: FC = ({ [assistants, removeAssistant, setActiveAssistant, onCreateDefaultAssistant] ) + useShortcut('switch_to_prev_main_tab', () => { + if (assistants.length > 1) { + const assistantIndex = assistants.findIndex((assistant) => assistant.id === activeAssistant?.id) + if (assistantIndex !== -1) { + onSwitchAssistant(assistants[assistantIndex === 0 ? assistants.length - 1 : assistantIndex - 1]).then() + } + } + }) + + useShortcut('switch_to_next_main_tab', () => { + if (assistants.length > 1) { + const assistantIndex = assistants.findIndex((assistant) => assistant.id === activeAssistant?.id) + if (assistantIndex !== -1) { + onSwitchAssistant(assistants[assistantIndex === assistants.length - 1 ? 0 : assistantIndex + 1]).then() + } + } + }) + return ( = ({ assistant: _assistant, activeTopic, setActiveTopic [assistant, assistants, onClearMessages, onDeleteTopic, onPinTopic, onMoveTopic, t, updateTopic] ) + useShortcut('switch_to_prev_main_tab', () => { + const index = assistant.topics.findIndex((topic) => topic.id === activeTopic.id) + if (index !== -1) { + onSwitchTopic(assistant.topics[index === 0 ? assistant.topics.length - 1 : index - 1]).then() + } + }) + + useShortcut('switch_to_next_main_tab', () => { + const index = assistant.topics.findIndex((topic) => topic.id === activeTopic.id) + if (index !== -1) { + onSwitchTopic(assistant.topics[index === assistant.topics.length - 1 ? 0 : index + 1]).then() + } + }) + return ( diff --git a/src/renderer/src/pages/home/Tabs/index.tsx b/src/renderer/src/pages/home/Tabs/index.tsx index a802475079..15161dc3b1 100644 --- a/src/renderer/src/pages/home/Tabs/index.tsx +++ b/src/renderer/src/pages/home/Tabs/index.tsx @@ -2,6 +2,7 @@ import { BarsOutlined, SettingOutlined } from '@ant-design/icons' import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup' import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant' import { useSettings } from '@renderer/hooks/useSettings' +import { useShortcut } from '@renderer/hooks/useShortcuts' import { useShowTopics } from '@renderer/hooks/useStore' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { Assistant, Topic } from '@renderer/types' @@ -52,6 +53,20 @@ const HomeTabs: FC = ({ activeAssistant, activeTopic, setActiveAssistant, icon: } + const segmentedOptions = [ + position === 'left' && topicPosition === 'left' ? assistantTab : undefined, + { + label: t('common.topics'), + value: 'topic', + icon: + }, + { + label: t('settings.title'), + value: 'settings', + icon: + } + ].filter(Boolean) as SegmentedProps['options'] + const onCreateAssistant = async () => { const assistant = await AddAssistantPopup.show() assistant && setActiveAssistant(assistant) @@ -93,6 +108,24 @@ const HomeTabs: FC = ({ activeAssistant, activeTopic, setActiveAssistant, } }, [position, tab, topicPosition]) + useShortcut('switch_to_prev_horizontal_tab', () => { + if (segmentedOptions.length > 1) { + const index = segmentedOptions.findIndex((option) => option.value === tab) + if (index !== -1) { + setTab(segmentedOptions[index === 0 ? segmentedOptions.length - 1 : index - 1].value as 'topic' | 'settings') + } + } + }) + + useShortcut('switch_to_next_horizontal_tab', () => { + if (segmentedOptions.length > 1) { + const index = segmentedOptions.findIndex((option) => option.value === tab) + if (index !== -1) { + setTab(segmentedOptions[index === segmentedOptions.length - 1 ? 0 : index + 1].value as 'topic' | 'settings') + } + } + }) + return ( {showTab && ( @@ -106,21 +139,7 @@ const HomeTabs: FC = ({ activeAssistant, activeTopic, setActiveAssistant, borderBottom: '0.5px solid var(--color-border)', gap: 2 }} - options={ - [ - position === 'left' && topicPosition === 'left' ? assistantTab : undefined, - { - label: t('common.topics'), - value: 'topic', - icon: - }, - { - label: t('settings.title'), - value: 'settings', - icon: - } - ].filter(Boolean) as SegmentedProps['options'] - } + options={segmentedOptions} onChange={(value) => setTab(value as 'topic' | 'settings')} block /> diff --git a/src/renderer/src/pages/knowledge/KnowledgePage.tsx b/src/renderer/src/pages/knowledge/KnowledgePage.tsx index 98b6acef2e..2cb54462ea 100644 --- a/src/renderer/src/pages/knowledge/KnowledgePage.tsx +++ b/src/renderer/src/pages/knowledge/KnowledgePage.tsx @@ -5,6 +5,7 @@ import ListItem from '@renderer/components/ListItem' import PromptPopup from '@renderer/components/Popups/PromptPopup' import Scrollbar from '@renderer/components/Scrollbar' import { useKnowledgeBases } from '@renderer/hooks/useKnowledge' +import { useShortcut } from '@renderer/hooks/useShortcuts' import { KnowledgeBase } from '@renderer/types' import { Dropdown, Empty, MenuProps } from 'antd' import { FC, useCallback, useEffect, useState } from 'react' @@ -81,6 +82,24 @@ const KnowledgePage: FC = () => { [deleteKnowledgeBase, renameKnowledgeBase, t] ) + useShortcut('switch_to_prev_main_tab', () => { + if (bases.length > 1) { + const index = bases.findIndex((item) => item.id === selectedBase?.id) + if (index !== -1) { + setSelectedBase(bases[index === 0 ? bases.length - 1 : index - 1]) + } + } + }) + + useShortcut('switch_to_next_main_tab', () => { + if (bases.length > 1) { + const index = bases.findIndex((item) => item.id === selectedBase?.id) + if (index !== -1) { + setSelectedBase(bases[index === bases.length - 1 ? 0 : index + 1]) + } + } + }) + return ( diff --git a/src/renderer/src/pages/settings/SettingsPage.tsx b/src/renderer/src/pages/settings/SettingsPage.tsx index f12a115af5..c4508dbf72 100644 --- a/src/renderer/src/pages/settings/SettingsPage.tsx +++ b/src/renderer/src/pages/settings/SettingsPage.tsx @@ -12,7 +12,7 @@ import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar' import { isLocalAi } from '@renderer/config/env' import { FC } from 'react' import { useTranslation } from 'react-i18next' -import { Link, Route, Routes, useLocation } from 'react-router-dom' +import { Link, Route, Routes, useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' import AboutSettings from './AboutSettings' @@ -23,13 +23,35 @@ import ModelSettings from '@renderer/pages/settings/ModelSettings/ModelSettings' import ProvidersList from './ProviderSettings' import QuickAssistantSettings from './QuickAssistantSettings' import ShortcutSettings from './ShortcutSettings' +import { useShortcut } from '@renderer/hooks/useShortcuts' import WebSearchSettings from './WebSearchSettings' +export let settingMenuItemPathList: string[] = [] + const SettingsPage: FC = () => { const { pathname } = useLocation() const { t } = useTranslation() + const navigate = useNavigate() + settingMenuItemPathList = [] + + const isRoute = (path: string) => { + settingMenuItemPathList.push(path) + return ((path: string): string => (pathname.startsWith(path) ? 'active' : ''))(path) + } + + useShortcut('switch_to_prev_main_tab', () => { + const index = settingMenuItemPathList.indexOf(pathname) + if (index !== -1) { + navigate(settingMenuItemPathList[index === 0 ? settingMenuItemPathList.length - 1 : index - 1]) + } + }) - const isRoute = (path: string): string => (pathname.startsWith(path) ? 'active' : '') + useShortcut('switch_to_next_main_tab', () => { + const index = settingMenuItemPathList.indexOf(pathname) + if (index !== -1) { + navigate(settingMenuItemPathList[index === settingMenuItemPathList.length - 1 ? 0 : index + 1]) + } + }) return ( diff --git a/src/renderer/src/pages/translate/TranslatePage.tsx b/src/renderer/src/pages/translate/TranslatePage.tsx index f82cdc0fd4..2f26582288 100644 --- a/src/renderer/src/pages/translate/TranslatePage.tsx +++ b/src/renderer/src/pages/translate/TranslatePage.tsx @@ -16,7 +16,7 @@ import { fetchTranslate } from '@renderer/services/ApiService' import { getDefaultTranslateAssistant } from '@renderer/services/AssistantService' import { Assistant, Message, TranslateHistory } from '@renderer/types' import { runAsyncFunction, uuid } from '@renderer/utils' -import { Button, Dropdown, Empty, Flex, Popconfirm, Select, Space, Tooltip } from 'antd' +import { Button, Dropdown, Empty, Flex, Popconfirm, Select, Space } from 'antd' import TextArea, { TextAreaRef } from 'antd/es/input/TextArea' import dayjs from 'dayjs' import { useLiveQuery } from 'dexie-react-hooks' @@ -139,13 +139,6 @@ const TranslatePage: FC = () => { }) }, []) - const onKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey && !e.metaKey) { - e.preventDefault() - onTranslate() - } - } - const SettingButton = () => { if (isLocalAi) { return null @@ -190,11 +183,13 @@ const TranslatePage: FC = () => { {t('translate.history.title')} - {!isEmpty(translateHistory) && ( + {translateHistory?.length && ( + onConfirm={clearHistory} + okText="Yes" + cancelText="No"> @@ -249,25 +244,14 @@ const TranslatePage: FC = () => { - - Enter: {t('translate.button.translate')} -
- Shift + Enter: {t('translate.tooltip.newline')} -
- }> - }> - {t('translate.button.translate')} - - + }> + {t('translate.button.translate')} +