{item.type === 'group' ? (
@@ -462,11 +480,11 @@ const VirtualizedRow = React.memo(
) : (
handleItemClick(item)}
- onMouseEnter={() => setFocusedItemKey(item.key)}>
+ onMouseOver={() => !isFocused && setFocusedItemKey(item.key)}>
{item.icon}
{item.name}
diff --git a/src/renderer/src/components/Popups/SelectModelPopup/reducer.ts b/src/renderer/src/components/Popups/SelectModelPopup/reducer.ts
index 45e3390ea8..974fc5b509 100644
--- a/src/renderer/src/components/Popups/SelectModelPopup/reducer.ts
+++ b/src/renderer/src/components/Popups/SelectModelPopup/reducer.ts
@@ -72,7 +72,7 @@ export const scrollReducer = (state: ScrollState, action: ScrollAction): ScrollS
scrollTrigger: action.payload.searchText ? 'search' : 'initial'
}
- case 'UPDATE_ON_LIST_CHANGE': {
+ case 'FOCUS_ON_LIST_CHANGE': {
const { modelItems } = action.payload
// 在列表变化时尝试聚焦一个模型:
@@ -96,13 +96,6 @@ export const scrollReducer = (state: ScrollState, action: ScrollAction): ScrollS
}
}
- case 'INIT_SCROLL':
- return {
- ...state,
- scrollTrigger: 'initial',
- lastScrollOffset: 0
- }
-
default:
return state
}
diff --git a/src/renderer/src/components/Popups/SelectModelPopup/types.ts b/src/renderer/src/components/Popups/SelectModelPopup/types.ts
index 41ec04c583..745e9688bb 100644
--- a/src/renderer/src/components/Popups/SelectModelPopup/types.ts
+++ b/src/renderer/src/components/Popups/SelectModelPopup/types.ts
@@ -38,5 +38,4 @@ export type ScrollAction =
| { type: 'FOCUS_NEXT_ITEM'; payload: { modelItems: FlatListItem[]; step: number } }
| { type: 'FOCUS_PAGE'; payload: { modelItems: FlatListItem[]; currentIndex: number; step: number } }
| { type: 'SEARCH_CHANGED'; payload: { searchText: string } }
- | { type: 'UPDATE_ON_LIST_CHANGE'; payload: { modelItems: FlatListItem[] } }
- | { type: 'INIT_SCROLL'; payload?: void }
+ | { type: 'FOCUS_ON_LIST_CHANGE'; payload: { modelItems: FlatListItem[] } }
diff --git a/src/renderer/src/components/Popups/UserPopup.tsx b/src/renderer/src/components/Popups/UserPopup.tsx
index 9d7569effa..ac4b9eca93 100644
--- a/src/renderer/src/components/Popups/UserPopup.tsx
+++ b/src/renderer/src/components/Popups/UserPopup.tsx
@@ -1,4 +1,5 @@
import DefaultAvatar from '@renderer/assets/images/avatar.png'
+import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import useAvatar from '@renderer/hooks/useAvatar'
import { useSettings } from '@renderer/hooks/useSettings'
import ImageStorage from '@renderer/services/ImageStorage'
@@ -154,7 +155,13 @@ const PopupContainer: React.FC = ({ resolve }) => {
}
}}
placement="bottom">
- {isEmoji(avatar) ? {avatar} : }
+ {isEmoji(avatar) ? (
+
+ {avatar}
+
+ ) : (
+
+ )}
@@ -182,23 +189,6 @@ const UserAvatar = styled(Avatar)`
}
`
-const EmojiAvatar = styled.div`
- cursor: pointer;
- width: 80px;
- height: 80px;
- border-radius: 20%;
- background-color: var(--color-background-soft);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 40px;
- transition: opacity 0.3s ease;
- border: 0.5px solid var(--color-border);
- &:hover {
- opacity: 0.8;
- }
-`
-
export default class UserPopup {
static topviewId = 0
static hide() {
diff --git a/src/renderer/src/components/QuickPanel/types.ts b/src/renderer/src/components/QuickPanel/types.ts
index e122aa1d29..7cef05be23 100644
--- a/src/renderer/src/components/QuickPanel/types.ts
+++ b/src/renderer/src/components/QuickPanel/types.ts
@@ -64,3 +64,5 @@ export interface QuickPanelContextType {
readonly beforeAction?: (Options: QuickPanelCallBackOptions) => void
readonly afterAction?: (Options: QuickPanelCallBackOptions) => void
}
+
+export type QuickPanelScrollTrigger = 'initial' | 'keyboard' | 'none'
diff --git a/src/renderer/src/components/QuickPanel/view.tsx b/src/renderer/src/components/QuickPanel/view.tsx
index 2bd1b14349..1602b6a4ac 100644
--- a/src/renderer/src/components/QuickPanel/view.tsx
+++ b/src/renderer/src/components/QuickPanel/view.tsx
@@ -6,13 +6,19 @@ import { theme } from 'antd'
import Color from 'color'
import { t } from 'i18next'
import { Check } from 'lucide-react'
-import React, { use, useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react'
+import React, { use, useCallback, useDeferredValue, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
import { FixedSizeList } from 'react-window'
import styled from 'styled-components'
import * as tinyPinyin from 'tiny-pinyin'
import { QuickPanelContext } from './provider'
-import { QuickPanelCallBackOptions, QuickPanelCloseAction, QuickPanelListItem, QuickPanelOpenOptions } from './types'
+import {
+ QuickPanelCallBackOptions,
+ QuickPanelCloseAction,
+ QuickPanelListItem,
+ QuickPanelOpenOptions,
+ QuickPanelScrollTrigger
+} from './types'
const ITEM_HEIGHT = 31
@@ -45,6 +51,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
// 避免上下翻页时,鼠标干扰
const [isMouseOver, setIsMouseOver] = useState(false)
+ const scrollTriggerRef = useRef('initial')
const [_index, setIndex] = useState(ctx.defaultIndex)
const index = useDeferredValue(_index)
const [historyPanel, setHistoryPanel] = useState([])
@@ -140,6 +147,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
(action?: QuickPanelCloseAction) => {
ctx.close(action)
setHistoryPanel([])
+ scrollTriggerRef.current = 'initial'
if (action === 'delete-symbol') {
const textArea = document.querySelector('.inputbar textarea') as HTMLTextAreaElement
@@ -249,10 +257,13 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ctx.isVisible])
- useEffect(() => {
- if (index >= 0) {
- listRef.current?.scrollToItem(index, 'auto')
- }
+ useLayoutEffect(() => {
+ if (!listRef.current || index < 0 || scrollTriggerRef.current === 'none') return
+
+ const alignment = scrollTriggerRef.current === 'keyboard' ? 'auto' : 'smart'
+ listRef.current?.scrollToItem(index, alignment)
+
+ scrollTriggerRef.current = 'none'
}, [index])
// 处理键盘事件
@@ -277,6 +288,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
switch (e.key) {
case 'ArrowUp':
+ scrollTriggerRef.current = 'keyboard'
if (isAssistiveKeyPressed) {
setIndex((prev) => {
const newIndex = prev - ctx.pageSize
@@ -289,6 +301,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
break
case 'ArrowDown':
+ scrollTriggerRef.current = 'keyboard'
if (isAssistiveKeyPressed) {
setIndex((prev) => {
const newIndex = prev + ctx.pageSize
@@ -301,6 +314,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
break
case 'PageUp':
+ scrollTriggerRef.current = 'keyboard'
setIndex((prev) => {
const newIndex = prev - ctx.pageSize
return newIndex < 0 ? 0 : newIndex
@@ -308,6 +322,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
break
case 'PageDown':
+ scrollTriggerRef.current = 'keyboard'
setIndex((prev) => {
const newIndex = prev + ctx.pageSize
return newIndex >= list.length ? list.length - 1 : newIndex
@@ -317,6 +332,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
case 'ArrowLeft':
if (!isAssistiveKeyPressed) return
if (!historyPanel.length) return
+ scrollTriggerRef.current = 'initial'
clearSearchText(false)
if (historyPanel.length > 0) {
const lastPanel = historyPanel.pop()
@@ -329,6 +345,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
case 'ArrowRight':
if (!isAssistiveKeyPressed) return
if (!list?.[index]?.isMenu) return
+ scrollTriggerRef.current = 'initial'
clearSearchText(false)
handleItemAction(list[index], 'enter')
break
@@ -413,7 +430,14 @@ export const QuickPanelView: React.FC = ({ setInputText }) => {
$selectedColor={selectedColor}
$selectedColorHover={selectedColorHover}
className={ctx.isVisible ? 'visible' : ''}>
- setIsMouseOver(true)}>
+
+ setIsMouseOver((prev) => {
+ scrollTriggerRef.current = 'initial'
+ return prev ? prev : true
+ })
+ }>
{
return (
{showKnowledgeIcon && (