diff --git a/src/renderer/src/components/CustomCollapse.tsx b/src/renderer/src/components/CustomCollapse.tsx index 9c94084d70..c6f4f79a78 100644 --- a/src/renderer/src/components/CustomCollapse.tsx +++ b/src/renderer/src/components/CustomCollapse.tsx @@ -1,5 +1,6 @@ import { Collapse } from 'antd' import { merge } from 'lodash' +import { ChevronRight } from 'lucide-react' import { FC, memo, useMemo, useState } from 'react' interface CustomCollapseProps { @@ -78,6 +79,14 @@ const CustomCollapse: FC = ({ destroyInactivePanel={destroyInactivePanel} collapsible={collapsible} onChange={setActiveKeys} + expandIcon={({ isActive }) => ( + + )} items={[ { styles: collapseItemStyles, diff --git a/src/renderer/src/components/Popups/SearchPopup.tsx b/src/renderer/src/components/Popups/SearchPopup.tsx index 3f0c59bf8a..16003c22ba 100644 --- a/src/renderer/src/components/Popups/SearchPopup.tsx +++ b/src/renderer/src/components/Popups/SearchPopup.tsx @@ -42,7 +42,7 @@ const PopupContainer: React.FC = ({ resolve }) => { paddingBottom: 16 }, body: { - height: '85vh', + height: '80vh', maxHeight: 'inherit', padding: 0 } diff --git a/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx b/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx index 74d16a80f0..e1420ba6cb 100644 --- a/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx +++ b/src/renderer/src/pages/home/Messages/Blocks/ThinkingBlock.tsx @@ -3,7 +3,7 @@ import { useSettings } from '@renderer/hooks/useSettings' import { MessageBlockStatus, type ThinkingMessageBlock } from '@renderer/types/newMessage' import { lightbulbVariants } from '@renderer/utils/motionVariants' import { Collapse, message as antdMessage, Tooltip } from 'antd' -import { Lightbulb } from 'lucide-react' +import { ChevronRight, Lightbulb } from 'lucide-react' import { motion } from 'motion/react' import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -57,6 +57,14 @@ const ThinkingBlock: React.FC = ({ block }) => { size="small" onChange={() => setActiveKey((key) => (key ? '' : 'thought'))} className="message-thought-container" + expandIcon={({ isActive }) => ( + + )} expandIconPosition="end" items={[ { diff --git a/src/renderer/src/pages/knowledge/components/KnowledgeSearchPopup.tsx b/src/renderer/src/pages/knowledge/components/KnowledgeSearchPopup.tsx index 34f750c908..f9018d7f49 100644 --- a/src/renderer/src/pages/knowledge/components/KnowledgeSearchPopup.tsx +++ b/src/renderer/src/pages/knowledge/components/KnowledgeSearchPopup.tsx @@ -1,14 +1,15 @@ import { CopyOutlined } from '@ant-design/icons' import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces' +import { HStack } from '@renderer/components/Layout' import { TopView } from '@renderer/components/TopView' import { searchKnowledgeBase } from '@renderer/services/KnowledgeService' import { FileType, KnowledgeBase } from '@renderer/types' -import { Input, List, message, Modal, Spin, Tooltip, Typography } from 'antd' -import { useRef, useState } from 'react' +import { Divider, Input, List, message, Modal, Spin, Tooltip, Typography } from 'antd' +import { Search } from 'lucide-react' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' -const { Search } = Input const { Text, Paragraph } = Typography interface ShowParams { @@ -25,7 +26,6 @@ const PopupContainer: React.FC = ({ base, resolve }) => { const [results, setResults] = useState>([]) const [searchKeyword, setSearchKeyword] = useState('') const { t } = useTranslation() - const searchInputRef = useRef(null) const handleSearch = async (value: string) => { if (!value.trim()) { @@ -84,77 +84,98 @@ const PopupContainer: React.FC = ({ base, resolve }) => { return ( visible && searchInputRef.current?.focus()} - width={800} + width={700} footer={null} centered - transitionName="animation-move-down"> - - + + + + + } + value={searchKeyword} + placeholder={t('knowledge.search')} allowClear - enterButton - size="large" - onSearch={handleSearch} - ref={searchInputRef} + autoFocus + spellCheck={false} + style={{ paddingLeft: 0 }} + variant="borderless" + size="middle" + onChange={(e) => setSearchKeyword(e.target.value)} + onPressEnter={() => handleSearch(searchKeyword)} /> - - {loading ? ( - - - - ) : ( - ( - - - - Score: {(item.score * 100).toFixed(1)}% - - handleCopy(item.pageContent)}> - - - - - {highlightText(item.pageContent)} - - - {t('knowledge.source')}:{' '} - {item.file ? ( - - {item.file.origin_name} - - ) : ( - item.metadata.source - )} - - - - - )} - /> - )} - - + + + + + {loading ? ( + + + + ) : ( + ( + + + + + {t('knowledge.source')}:{' '} + {item.file ? ( + + {item.file.origin_name} + + ) : ( + item.metadata.source + )} + + Score: {(item.score * 100).toFixed(1)}% + + + + handleCopy(item.pageContent)}> + + + + + + {highlightText(item.pageContent)} + + + + )} + /> + )} + ) } -const SearchContainer = styled.div` - display: flex; - flex-direction: column; - gap: 20px; -` - const ResultsContainer = styled.div` - max-height: 60vh; + padding: 0 16px; overflow-y: auto; + max-height: 70vh; ` const LoadingContainer = styled.div` @@ -164,21 +185,29 @@ const LoadingContainer = styled.div` height: 200px; ` +const TagContainer = styled.div` + position: absolute; + top: 58px; + right: 16px; + display: flex; + align-items: center; + gap: 8px; + opacity: 0; + transition: opacity 0.2s; +` + const ResultItem = styled.div` width: 100%; position: relative; padding: 16px; background: var(--color-background-soft); border-radius: 8px; -` -const TagContainer = styled.div` - position: absolute; - top: 8px; - right: 8px; - display: flex; - align-items: center; - gap: 8px; + &:hover { + ${TagContainer} { + opacity: 1 !important; + } + } ` const ScoreTag = styled.div` @@ -187,6 +216,7 @@ const ScoreTag = styled.div` color: white; border-radius: 4px; font-size: 12px; + flex-shrink: 0; ` const CopyButton = styled.div` @@ -195,7 +225,7 @@ const CopyButton = styled.div` justify-content: center; width: 24px; height: 24px; - background: var(--color-background); + background: var(--color-background-mute); color: var(--color-text); border-radius: 4px; cursor: pointer; @@ -208,12 +238,35 @@ const CopyButton = styled.div` ` const MetadataContainer = styled.div` - margin-top: 8px; - padding-top: 8px; - border-top: 1px solid var(--color-border); + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + margin-bottom: 8px; + padding-bottom: 8px; + border-bottom: 1px solid var(--color-border); user-select: text; ` +const SearchIcon = styled.div` + width: 32px; + height: 32px; + border-radius: 50%; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + background-color: var(--color-background-soft); + margin-right: 2px; + &.back-icon { + cursor: pointer; + transition: background-color 0.2s; + &:hover { + background-color: var(--color-background-mute); + } + } +` + const TopViewKey = 'KnowledgeSearchPopup' export default class KnowledgeSearchPopup { diff --git a/src/renderer/src/pages/knowledge/components/KnowledgeSettingsPopup.tsx b/src/renderer/src/pages/knowledge/components/KnowledgeSettingsPopup.tsx index bd652680ce..625ca2c90f 100644 --- a/src/renderer/src/pages/knowledge/components/KnowledgeSettingsPopup.tsx +++ b/src/renderer/src/pages/knowledge/components/KnowledgeSettingsPopup.tsx @@ -15,7 +15,6 @@ import { sortBy } from 'lodash' import { ChevronDown } from 'lucide-react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import styled from 'styled-components' interface ShowParams { base: KnowledgeBase @@ -271,15 +270,6 @@ const PopupContainer: React.FC = ({ base: _base, resolve }) => { const TopViewKey = 'KnowledgeSettingsPopup' -const AdvancedSettingsButton = styled.div` - cursor: pointer; - margin-bottom: 16px; - margin-top: -10px; - color: var(--color-primary); - display: flex; - align-items: center; -` - export default class KnowledgeSettingsPopup { static hide() { TopView.hide(TopViewKey)