diff --git a/src/renderer/src/components/QuickPanel/view.tsx b/src/renderer/src/components/QuickPanel/view.tsx index 0a2cdd0a..b516dd8f 100644 --- a/src/renderer/src/components/QuickPanel/view.tsx +++ b/src/renderer/src/components/QuickPanel/view.tsx @@ -34,6 +34,7 @@ export const QuickPanelView: React.FC<{ const bodyRef = useRef(null) const contentRef = useRef(null) + const footerRef = useRef(null) const scrollBlock = useRef('nearest') @@ -343,6 +344,20 @@ export const QuickPanelView: React.FC<{ } }, [index, isAssistiveKeyPressed, historyPanel, ctx, list, handleItemAction, handleClose, clearSearchText]) + const [footerWidth, setFooterWidth] = useState(0) + useEffect(() => { + if (!footerRef.current || !ctx.isVisible) return + const footerWidth = footerRef.current.clientWidth + setFooterWidth(footerWidth) + + const handleResize = () => { + const footerWidth = footerRef.current!.clientWidth + setFooterWidth(footerWidth) + } + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, [ctx.isVisible]) + return ( setIsMouseOver(true)}> @@ -355,7 +370,10 @@ export const QuickPanelView: React.FC<{ disabled: item.disabled })} key={i} - onClick={() => handleItemAction(item, 'click')} + onClick={(e) => { + e.stopPropagation() + handleItemAction(item, 'click') + }} onMouseEnter={() => setIndex(i)}> {item.icon} @@ -377,45 +395,47 @@ export const QuickPanelView: React.FC<{ ))} - - - {ctx.title || ''} - - ESC {t('settings.quickPanel.close')} + + {ctx.title || ''} + + ESC {t('settings.quickPanel.close')} - - ▲▼ {t('settings.quickPanel.select')} - + + ▲▼ {t('settings.quickPanel.select')} + + {footerWidth >= 500 && ( + <> + + + {ASSISTIVE_KEY} + + + ▲▼ {t('settings.quickPanel.page')} + + + {canForwardAndBackward && ( + + + {ASSISTIVE_KEY} + + + ◀︎▶︎ {t('settings.quickPanel.back')}/{t('settings.quickPanel.forward')} + + )} + + )} + + + ↩︎ {t('settings.quickPanel.confirm')} + + + {ctx.multiple && ( {ASSISTIVE_KEY} - + ▲▼ {t('settings.quickPanel.page')} + + ↩︎ {t('settings.quickPanel.multiple')} - - {canForwardAndBackward && ( - - - {ASSISTIVE_KEY} - - + ◀︎▶︎ {t('settings.quickPanel.back')}/{t('settings.quickPanel.forward')} - - )} - - - ↩︎ {t('settings.quickPanel.confirm')} - - - {ctx.multiple && ( - - - {ASSISTIVE_KEY} - - + ↩︎ {t('settings.quickPanel.multiple')} - - )} - + )} @@ -462,22 +482,30 @@ const QuickPanelBody = styled.div` ` const QuickPanelFooter = styled.div` - width: 100%; -` - -const QuickPanelFooterTips = styled.div` display: flex; - align-items: center; + width: 100%; justify-content: space-between; - gap: 8px; - font-size: 10px; - color: var(--color-text-3); + align-items: center; + gap: 16px; padding: 8px 12px 5px; ` -const QuickPanelTitle = styled.div` +const QuickPanelFooterTips = styled.div<{ $footerWidth: number }>` + display: flex; + align-items: center; + justify-content: flex-end; + flex-shrink: 0; + gap: 16px; + font-size: 10px; + color: var(--color-text-3); +` + +const QuickPanelFooterTitle = styled.div` font-size: 11px; color: var(--color-text-3); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; ` const QuickPanelContent = styled.div<{ $pageSize: number; $isMouseOver: boolean }>` diff --git a/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx b/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx index 3597a582..64e26488 100644 --- a/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx @@ -54,8 +54,10 @@ const AttachmentButton: FC = ({ ref, model, files, setFiles, ToolbarButto placement="top" title={isVisionModel(model) ? t('chat.input.upload') : t('chat.input.upload.document')} arrow> - - + + ) diff --git a/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx b/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx index 5873ab29..6ed77ee3 100644 --- a/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx +++ b/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx @@ -1,8 +1,9 @@ +import { FileOutlined } from '@ant-design/icons' import FileManager from '@renderer/services/FileManager' import { FileType } from '@renderer/types' -import { Upload as AntdUpload, UploadFile } from 'antd' +import { ConfigProvider, Image, Tag } from 'antd' import { isEmpty } from 'lodash' -import { FC } from 'react' +import { FC, useState } from 'react' import styled from 'styled-components' interface Props { @@ -11,39 +12,79 @@ interface Props { } const AttachmentPreview: FC = ({ files, setFiles }) => { + const [visibleId, setVisibleId] = useState('') + + const isImage = (ext: string) => { + return ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp'].includes(ext) + } + if (isEmpty(files)) { return null } return ( - 20 ? 'text' : 'picture-card'} - fileList={files.map( - (file) => - ({ - uid: file.id, - url: 'file://' + FileManager.getSafePath(file), - status: 'done', - name: file.origin_name || file.name - }) as UploadFile - )} - onRemove={(item) => setFiles(files.filter((file) => item.uid !== file.id))} - /> + + {files.map((file) => ( + } + bordered={false} + color="cyan" + closable + onClose={() => setFiles(files.filter((f) => f.id !== file.id))}> + { + if (isImage(file.ext)) { + setVisibleId(file.id) + return + } + const path = FileManager.getSafePath(file) + if (path) { + window.api.file.openPath(path) + } + }}> + {file.origin_name || file.name} + {isImage(file.ext) && ( + { + setVisibleId(value ? file.id : '') + } + }} + /> + )} + + + ))} + ) } const ContentContainer = styled.div` - max-height: 40vh; - overflow-y: auto; width: 100%; - padding: 10px 15px 0; + display: flex; + flex-wrap: wrap; + gap: 4px 0; + padding: 5px 15px 0; ` -const Upload = styled(AntdUpload)` - .ant-upload-list-item { - background-color: var(--color-background); +const FileName = styled.span` + cursor: pointer; + &:hover { + text-decoration: underline; } ` diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index bc671015..727a80fc 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -479,6 +479,11 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = return newSelectedKnowledgeBases }) return event.preventDefault() + + if (event.key === 'Backspace' && text.trim() === '' && files.length > 0) { + setFiles((prev) => prev.slice(0, -1)) + return event.preventDefault() + } } }