mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-06 05:09:09 +08:00
refactor(AddKnowledgePopup): streamline settings panel and enhance advanced options
- Removed the left menu and integrated settings directly into the main panel for a more cohesive user experience. - Introduced a toggle for advanced settings, allowing users to expand or collapse additional configuration options. - Updated layout and styling for improved usability, including adjustments to padding and margins. - Enhanced scroll behavior for the advanced settings section to ensure visibility when expanded. - Minor adjustments to component imports and state management for better performance and clarity.
This commit is contained in:
parent
f163ace86c
commit
3bced85fc3
@ -6,7 +6,6 @@ import { DEFAULT_KNOWLEDGE_DOCUMENT_COUNT, isMac } from '@renderer/config/consta
|
|||||||
import { getEmbeddingMaxContext } from '@renderer/config/embedings'
|
import { getEmbeddingMaxContext } from '@renderer/config/embedings'
|
||||||
import { isEmbeddingModel, isRerankModel } from '@renderer/config/models'
|
import { isEmbeddingModel, isRerankModel } from '@renderer/config/models'
|
||||||
import { NOT_SUPPORTED_REANK_PROVIDERS } from '@renderer/config/providers'
|
import { NOT_SUPPORTED_REANK_PROVIDERS } from '@renderer/config/providers'
|
||||||
// import { SUPPORTED_REANK_PROVIDERS } from '@renderer/config/providers'
|
|
||||||
import { useKnowledgeBases } from '@renderer/hooks/useKnowledge'
|
import { useKnowledgeBases } from '@renderer/hooks/useKnowledge'
|
||||||
import { useOcrProviders } from '@renderer/hooks/useOcr'
|
import { useOcrProviders } from '@renderer/hooks/useOcr'
|
||||||
import { usePreprocessProviders } from '@renderer/hooks/usePreprocess'
|
import { usePreprocessProviders } from '@renderer/hooks/usePreprocess'
|
||||||
@ -15,10 +14,11 @@ import { getKnowledgeBaseParams } from '@renderer/services/KnowledgeService'
|
|||||||
import { getModelUniqId } from '@renderer/services/ModelService'
|
import { getModelUniqId } from '@renderer/services/ModelService'
|
||||||
import { KnowledgeBase, Model, OcrProvider, PreprocessProvider } from '@renderer/types'
|
import { KnowledgeBase, Model, OcrProvider, PreprocessProvider } from '@renderer/types'
|
||||||
import { getErrorMessage } from '@renderer/utils/error'
|
import { getErrorMessage } from '@renderer/utils/error'
|
||||||
import { Alert, Input, InputNumber, Menu, Modal, Select, Slider, Switch, Tooltip } from 'antd'
|
import { Alert, Input, InputNumber, Modal, Select, Slider, Switch, Tooltip } from 'antd'
|
||||||
import { find, sortBy } from 'lodash'
|
import { find, sortBy } from 'lodash'
|
||||||
|
import { ChevronDown } from 'lucide-react'
|
||||||
import { nanoid } from 'nanoid'
|
import { nanoid } from 'nanoid'
|
||||||
import { useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
@ -34,12 +34,12 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
|
|||||||
const [open, setOpen] = useState(true)
|
const [open, setOpen] = useState(true)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [autoDims, setAutoDims] = useState(true)
|
const [autoDims, setAutoDims] = useState(true)
|
||||||
|
const [showAdvanced, setShowAdvanced] = useState(false)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { providers } = useProviders()
|
const { providers } = useProviders()
|
||||||
const { addKnowledgeBase } = useKnowledgeBases()
|
const { addKnowledgeBase } = useKnowledgeBases()
|
||||||
const [newBase, setNewBase] = useState<KnowledgeBase>({} as KnowledgeBase)
|
const [newBase, setNewBase] = useState<KnowledgeBase>({} as KnowledgeBase)
|
||||||
const [dimensions, setDimensions] = useState<number | undefined>(undefined)
|
const [dimensions, setDimensions] = useState<number | undefined>(undefined)
|
||||||
const [selectedMenu, setSelectedMenu] = useState('general')
|
|
||||||
|
|
||||||
const { preprocessProviders } = usePreprocessProviders()
|
const { preprocessProviders } = usePreprocessProviders()
|
||||||
const { ocrProviders } = useOcrProviders()
|
const { ocrProviders } = useOcrProviders()
|
||||||
@ -60,6 +60,7 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
|
|||||||
}, [providers])
|
}, [providers])
|
||||||
|
|
||||||
const nameInputRef = useRef<any>(null)
|
const nameInputRef = useRef<any>(null)
|
||||||
|
const scrollContainerRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const embeddingSelectOptions = useMemo(() => {
|
const embeddingSelectOptions = useMemo(() => {
|
||||||
return providers
|
return providers
|
||||||
@ -186,252 +187,19 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
|
|||||||
resolve(null)
|
resolve(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
const menuItems = [
|
useEffect(() => {
|
||||||
{
|
if (showAdvanced && scrollContainerRef.current) {
|
||||||
key: 'general',
|
// 延迟滚动,确保DOM更新完成
|
||||||
label: t('settings.general')
|
setTimeout(() => {
|
||||||
},
|
if (scrollContainerRef.current) {
|
||||||
{
|
scrollContainerRef.current.scrollTo({
|
||||||
key: 'advanced',
|
top: scrollContainerRef.current.scrollHeight,
|
||||||
label: t('settings.advanced.title')
|
behavior: 'smooth'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
}
|
}
|
||||||
]
|
}, [showAdvanced])
|
||||||
|
|
||||||
const renderSettings = () => {
|
|
||||||
if (selectedMenu === 'general') {
|
|
||||||
return (
|
|
||||||
<SettingsPanel>
|
|
||||||
<SettingsItem>
|
|
||||||
<div className="settings-label">{t('common.name')}</div>
|
|
||||||
<Input
|
|
||||||
ref={nameInputRef}
|
|
||||||
placeholder={t('common.name')}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.value) {
|
|
||||||
setNewBase({ ...newBase, name: e.target.value })
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
<SettingsItem>
|
|
||||||
<div className="settings-label">
|
|
||||||
{t('settings.tool.preprocess.title')} / {t('settings.tool.ocr.title')}
|
|
||||||
<Tooltip title={t('settings.tool.preprocessOrOcr.tooltip')} placement="right">
|
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<Select
|
|
||||||
value={selectedProvider?.id}
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
onChange={(value: string) => {
|
|
||||||
const type = preprocessProviders.find((p) => p.id === value) ? 'preprocess' : 'ocr'
|
|
||||||
const provider = (type === 'preprocess' ? preprocessProviders : ocrProviders).find(
|
|
||||||
(p) => p.id === value
|
|
||||||
)
|
|
||||||
if (!provider) {
|
|
||||||
setSelectedProvider(undefined)
|
|
||||||
setNewBase({
|
|
||||||
...newBase,
|
|
||||||
preprocessOrOcrProvider: undefined
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
setSelectedProvider(provider)
|
|
||||||
setNewBase({
|
|
||||||
...newBase,
|
|
||||||
preprocessOrOcrProvider: {
|
|
||||||
type: type,
|
|
||||||
provider: provider
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
placeholder={t('settings.tool.preprocess.provider_placeholder')}
|
|
||||||
options={preprocessOrOcrSelectOptions}
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
<SettingsItem>
|
|
||||||
<div className="settings-label">
|
|
||||||
{t('models.embedding_model')}
|
|
||||||
<Tooltip title={t('models.embedding_model_tooltip')} placement="right">
|
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<Select
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
options={embeddingSelectOptions}
|
|
||||||
placeholder={t('settings.models.empty')}
|
|
||||||
onChange={(value) => {
|
|
||||||
const model = value
|
|
||||||
? providers.flatMap((p) => p.models).find((m) => getModelUniqId(m) === value)
|
|
||||||
: undefined
|
|
||||||
if (!model) return
|
|
||||||
setNewBase({ ...newBase, model })
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
<SettingsItem>
|
|
||||||
<div className="settings-label">
|
|
||||||
{t('models.rerank_model')}
|
|
||||||
<Tooltip title={t('models.rerank_model_tooltip')} placement="right">
|
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<Select
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
options={rerankSelectOptions}
|
|
||||||
placeholder={t('settings.models.empty')}
|
|
||||||
onChange={(value) => {
|
|
||||||
const rerankModel = value
|
|
||||||
? providers.flatMap((p) => p.models).find((m) => getModelUniqId(m) === value)
|
|
||||||
: undefined
|
|
||||||
setNewBase({ ...newBase, rerankModel })
|
|
||||||
}}
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
<SettingsItem>
|
|
||||||
<div className="settings-label">
|
|
||||||
{t('knowledge.document_count')}
|
|
||||||
<Tooltip title={t('knowledge.document_count_help')}>
|
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<Slider
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
min={1}
|
|
||||||
max={30}
|
|
||||||
step={1}
|
|
||||||
defaultValue={DEFAULT_KNOWLEDGE_DOCUMENT_COUNT}
|
|
||||||
marks={{ 1: '1', 6: t('knowledge.document_count_default'), 30: '30' }}
|
|
||||||
onChange={(value) => setNewBase({ ...newBase, documentCount: value })}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
{/* dimensions */}
|
|
||||||
<SettingsItem>
|
|
||||||
<div
|
|
||||||
className="settings-label"
|
|
||||||
style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
|
|
||||||
<span>
|
|
||||||
{t('knowledge.dimensions_auto_set')}
|
|
||||||
<Tooltip title={t('knowledge.dimensions_default')} placement="right">
|
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
||||||
</Tooltip>
|
|
||||||
</span>
|
|
||||||
<Switch
|
|
||||||
checked={autoDims}
|
|
||||||
onChange={(checked) => {
|
|
||||||
setAutoDims(checked)
|
|
||||||
if (checked) {
|
|
||||||
setDimensions(undefined)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
{!autoDims && (
|
|
||||||
<SettingsItem>
|
|
||||||
<div className="settings-label">
|
|
||||||
{t('knowledge.dimensions')}
|
|
||||||
<Tooltip title={t('knowledge.dimensions_size_tooltip')} placement="right">
|
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<InputNumber
|
|
||||||
min={1}
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
placeholder={t('knowledge.dimensions_size_placeholder')}
|
|
||||||
value={newBase.dimensions}
|
|
||||||
onChange={(value) => {
|
|
||||||
setDimensions(value === null ? undefined : value)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
)}
|
|
||||||
</SettingsPanel>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (selectedMenu === 'advanced') {
|
|
||||||
return (
|
|
||||||
<SettingsPanel>
|
|
||||||
<SettingsItem>
|
|
||||||
<div className="settings-label">
|
|
||||||
{t('knowledge.chunk_size')}
|
|
||||||
<Tooltip title={t('knowledge.chunk_size_tooltip')} placement="right">
|
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<InputNumber
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
min={100}
|
|
||||||
value={newBase.chunkSize}
|
|
||||||
placeholder={t('knowledge.chunk_size_placeholder')}
|
|
||||||
onChange={(value) => {
|
|
||||||
const maxContext = getEmbeddingMaxContext(newBase.model.id)
|
|
||||||
if (!value || !maxContext || value <= maxContext) {
|
|
||||||
setNewBase({ ...newBase, chunkSize: value || undefined })
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
<SettingsItem>
|
|
||||||
<div className="settings-label">
|
|
||||||
{t('knowledge.chunk_overlap')}
|
|
||||||
<Tooltip title={t('knowledge.chunk_overlap_tooltip')} placement="right">
|
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<InputNumber
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
min={0}
|
|
||||||
value={newBase.chunkOverlap}
|
|
||||||
placeholder={t('knowledge.chunk_overlap_placeholder')}
|
|
||||||
onChange={async (value) => {
|
|
||||||
if (!value || (newBase.chunkSize && newBase.chunkSize > value)) {
|
|
||||||
setNewBase({ ...newBase, chunkOverlap: value || undefined })
|
|
||||||
} else {
|
|
||||||
await window.message.error(t('message.error.chunk_overlap_too_large'))
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
<SettingsItem>
|
|
||||||
<div className="settings-label">
|
|
||||||
{t('knowledge.threshold')}
|
|
||||||
<Tooltip title={t('knowledge.threshold_tooltip')} placement="right">
|
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<InputNumber
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
step={0.1}
|
|
||||||
min={0}
|
|
||||||
max={1}
|
|
||||||
value={newBase.threshold}
|
|
||||||
placeholder={t('knowledge.threshold_placeholder')}
|
|
||||||
onChange={(value) => setNewBase({ ...newBase, threshold: value || undefined })}
|
|
||||||
/>
|
|
||||||
</SettingsItem>
|
|
||||||
|
|
||||||
<Alert
|
|
||||||
message={t('knowledge.chunk_size_change_warning')}
|
|
||||||
type="warning"
|
|
||||||
showIcon
|
|
||||||
icon={<WarningOutlined />}
|
|
||||||
/>
|
|
||||||
</SettingsPanel>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsModal
|
<SettingsModal
|
||||||
@ -443,10 +211,11 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
|
|||||||
afterOpenChange={(visible) => visible && nameInputRef.current?.focus()}
|
afterOpenChange={(visible) => visible && nameInputRef.current?.focus()}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
centered
|
centered
|
||||||
|
transitionName="animation-move-down"
|
||||||
okButtonProps={{ loading }}
|
okButtonProps={{ loading }}
|
||||||
width="min(800px, 70vw)"
|
width="min(600px, 60vw)"
|
||||||
styles={{
|
styles={{
|
||||||
body: { padding: 0, height: '65vh' },
|
body: { padding: 0 },
|
||||||
header: {
|
header: {
|
||||||
padding: '10px 15px',
|
padding: '10px 15px',
|
||||||
borderBottom: '0.5px solid var(--color-border)',
|
borderBottom: '0.5px solid var(--color-border)',
|
||||||
@ -455,19 +224,252 @@ const PopupContainer: React.FC<Props> = ({ title, resolve }) => {
|
|||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
|
paddingBottom: 10,
|
||||||
overflow: 'hidden'
|
overflow: 'hidden'
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<HStack>
|
<HStack>
|
||||||
<LeftMenu>
|
<SettingsContentPanel ref={scrollContainerRef}>
|
||||||
<StyledMenu
|
<SettingsPanel>
|
||||||
defaultSelectedKeys={['general']}
|
<SettingsItem>
|
||||||
mode="vertical"
|
<div className="settings-label">{t('common.name')}</div>
|
||||||
items={menuItems}
|
<Input
|
||||||
onSelect={({ key }) => setSelectedMenu(key)}
|
ref={nameInputRef}
|
||||||
/>
|
placeholder={t('common.name')}
|
||||||
</LeftMenu>
|
onChange={(e) => {
|
||||||
<SettingsContentPanel>{renderSettings()}</SettingsContentPanel>
|
if (e.target.value) {
|
||||||
|
setNewBase({ ...newBase, name: e.target.value })
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<SettingsItem>
|
||||||
|
<div className="settings-label">
|
||||||
|
{t('settings.tool.preprocess.title')} / {t('settings.tool.ocr.title')}
|
||||||
|
<Tooltip title={t('settings.tool.preprocessOrOcr.tooltip')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
value={selectedProvider?.id}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
onChange={(value: string) => {
|
||||||
|
const type = preprocessProviders.find((p) => p.id === value) ? 'preprocess' : 'ocr'
|
||||||
|
const provider = (type === 'preprocess' ? preprocessProviders : ocrProviders).find(
|
||||||
|
(p) => p.id === value
|
||||||
|
)
|
||||||
|
if (!provider) {
|
||||||
|
setSelectedProvider(undefined)
|
||||||
|
setNewBase({
|
||||||
|
...newBase,
|
||||||
|
preprocessOrOcrProvider: undefined
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setSelectedProvider(provider)
|
||||||
|
setNewBase({
|
||||||
|
...newBase,
|
||||||
|
preprocessOrOcrProvider: {
|
||||||
|
type: type,
|
||||||
|
provider: provider
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
placeholder={t('settings.tool.preprocess.provider_placeholder')}
|
||||||
|
options={preprocessOrOcrSelectOptions}
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<SettingsItem>
|
||||||
|
<div className="settings-label">
|
||||||
|
{t('models.embedding_model')}
|
||||||
|
<Tooltip title={t('models.embedding_model_tooltip')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
options={embeddingSelectOptions}
|
||||||
|
placeholder={t('settings.models.empty')}
|
||||||
|
onChange={(value) => {
|
||||||
|
const model = value
|
||||||
|
? providers.flatMap((p) => p.models).find((m) => getModelUniqId(m) === value)
|
||||||
|
: undefined
|
||||||
|
if (!model) return
|
||||||
|
setNewBase({ ...newBase, model })
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<SettingsItem>
|
||||||
|
<div className="settings-label">
|
||||||
|
{t('models.rerank_model')}
|
||||||
|
<Tooltip title={t('models.rerank_model_tooltip')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<Select
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
options={rerankSelectOptions}
|
||||||
|
placeholder={t('settings.models.empty')}
|
||||||
|
onChange={(value) => {
|
||||||
|
const rerankModel = value
|
||||||
|
? providers.flatMap((p) => p.models).find((m) => getModelUniqId(m) === value)
|
||||||
|
: undefined
|
||||||
|
setNewBase({ ...newBase, rerankModel })
|
||||||
|
}}
|
||||||
|
allowClear
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<SettingsItem>
|
||||||
|
<div className="settings-label">
|
||||||
|
{t('knowledge.document_count')}
|
||||||
|
<Tooltip title={t('knowledge.document_count_help')}>
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<Slider
|
||||||
|
min={1}
|
||||||
|
max={30}
|
||||||
|
step={1}
|
||||||
|
defaultValue={DEFAULT_KNOWLEDGE_DOCUMENT_COUNT}
|
||||||
|
marks={{ 1: '1', 6: t('knowledge.document_count_default'), 30: '30' }}
|
||||||
|
onChange={(value) => setNewBase({ ...newBase, documentCount: value })}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
{/* dimensions */}
|
||||||
|
<SettingsItem style={{ marginTop: 35 }}>
|
||||||
|
<div
|
||||||
|
className="settings-label"
|
||||||
|
style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%' }}>
|
||||||
|
<span>
|
||||||
|
{t('knowledge.dimensions_auto_set')}
|
||||||
|
<Tooltip title={t('knowledge.dimensions_default')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</span>
|
||||||
|
<Switch
|
||||||
|
checked={autoDims}
|
||||||
|
onChange={(checked) => {
|
||||||
|
setAutoDims(checked)
|
||||||
|
if (checked) {
|
||||||
|
setDimensions(undefined)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
{!autoDims && (
|
||||||
|
<SettingsItem>
|
||||||
|
<div className="settings-label">
|
||||||
|
{t('knowledge.dimensions')}
|
||||||
|
<Tooltip title={t('knowledge.dimensions_size_tooltip')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<InputNumber
|
||||||
|
min={1}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
placeholder={t('knowledge.dimensions_size_placeholder')}
|
||||||
|
value={newBase.dimensions}
|
||||||
|
onChange={(value) => {
|
||||||
|
setDimensions(value === null ? undefined : value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
)}
|
||||||
|
</SettingsPanel>
|
||||||
|
|
||||||
|
<AdvancedSettingsButton onClick={() => setShowAdvanced(!showAdvanced)}>
|
||||||
|
<ChevronDown
|
||||||
|
size={18}
|
||||||
|
style={{
|
||||||
|
transform: showAdvanced ? 'rotate(180deg)' : 'rotate(0deg)',
|
||||||
|
transition: 'transform 0.3s',
|
||||||
|
marginRight: 8,
|
||||||
|
stroke: 'var(--color-primary)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{t('common.advanced_settings')}
|
||||||
|
</AdvancedSettingsButton>
|
||||||
|
|
||||||
|
{showAdvanced && (
|
||||||
|
<SettingsPanel>
|
||||||
|
<SettingsItem>
|
||||||
|
<div className="settings-label">
|
||||||
|
{t('knowledge.chunk_size')}
|
||||||
|
<Tooltip title={t('knowledge.chunk_size_tooltip')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<InputNumber
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
min={100}
|
||||||
|
value={newBase.chunkSize}
|
||||||
|
placeholder={t('knowledge.chunk_size_placeholder')}
|
||||||
|
onChange={(value) => {
|
||||||
|
const maxContext = getEmbeddingMaxContext(newBase.model.id)
|
||||||
|
if (!value || !maxContext || value <= maxContext) {
|
||||||
|
setNewBase({ ...newBase, chunkSize: value || undefined })
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<SettingsItem>
|
||||||
|
<div className="settings-label">
|
||||||
|
{t('knowledge.chunk_overlap')}
|
||||||
|
<Tooltip title={t('knowledge.chunk_overlap_tooltip')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<InputNumber
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
min={0}
|
||||||
|
value={newBase.chunkOverlap}
|
||||||
|
placeholder={t('knowledge.chunk_overlap_placeholder')}
|
||||||
|
onChange={async (value) => {
|
||||||
|
if (!value || (newBase.chunkSize && newBase.chunkSize > value)) {
|
||||||
|
setNewBase({ ...newBase, chunkOverlap: value || undefined })
|
||||||
|
} else {
|
||||||
|
await window.message.error(t('message.error.chunk_overlap_too_large'))
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<SettingsItem>
|
||||||
|
<div className="settings-label">
|
||||||
|
{t('knowledge.threshold')}
|
||||||
|
<Tooltip title={t('knowledge.threshold_tooltip')} placement="right">
|
||||||
|
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<InputNumber
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
step={0.1}
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
value={newBase.threshold}
|
||||||
|
placeholder={t('knowledge.threshold_placeholder')}
|
||||||
|
onChange={(value) => setNewBase({ ...newBase, threshold: value || undefined })}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
<Alert
|
||||||
|
message={t('knowledge.chunk_size_change_warning')}
|
||||||
|
type="warning"
|
||||||
|
showIcon
|
||||||
|
icon={<WarningOutlined />}
|
||||||
|
/>
|
||||||
|
</SettingsPanel>
|
||||||
|
)}
|
||||||
|
</SettingsContentPanel>
|
||||||
</HStack>
|
</HStack>
|
||||||
</SettingsModal>
|
</SettingsModal>
|
||||||
)
|
)
|
||||||
@ -521,27 +523,22 @@ const SettingsModal = styled(Modal)`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const LeftMenu = styled.div`
|
|
||||||
height: 65vh;
|
|
||||||
border-right: 0.5px solid var(--color-border);
|
|
||||||
`
|
|
||||||
|
|
||||||
const SettingsContentPanel = styled.div`
|
const SettingsContentPanel = styled.div`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 16px 16px;
|
padding: 16px 16px;
|
||||||
height: calc(65vh - 16px);
|
max-height: calc(80vh - 80px);
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
`
|
`
|
||||||
|
|
||||||
const StyledMenu = styled(Menu)`
|
const AdvancedSettingsButton = styled.div`
|
||||||
width: 220px;
|
cursor: pointer;
|
||||||
padding: 5px;
|
margin-bottom: 16px;
|
||||||
background: transparent;
|
color: var(--color-primary);
|
||||||
margin-top: 2px;
|
display: flex;
|
||||||
border-inline-end: none !important;
|
align-items: center;
|
||||||
.ant-menu-item {
|
margin: 0 16px;
|
||||||
margin-bottom: 7px;
|
padding: 16px 0;
|
||||||
}
|
border-top: 0.5px solid var(--color-border);
|
||||||
`
|
`
|
||||||
|
|
||||||
export default class AddKnowledgePopup {
|
export default class AddKnowledgePopup {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { InfoCircleOutlined, SettingOutlined, WarningOutlined } from '@ant-design/icons'
|
import { InfoCircleOutlined, WarningOutlined } from '@ant-design/icons'
|
||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { DEFAULT_KNOWLEDGE_DOCUMENT_COUNT, isMac } from '@renderer/config/constant'
|
import { DEFAULT_KNOWLEDGE_DOCUMENT_COUNT, isMac } from '@renderer/config/constant'
|
||||||
@ -113,13 +113,11 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
const menuItems = [
|
const menuItems = [
|
||||||
{
|
{
|
||||||
key: 'general',
|
key: 'general',
|
||||||
label: t('settings.general'),
|
label: t('settings.general')
|
||||||
icon: <SettingOutlined />
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'advanced',
|
key: 'advanced',
|
||||||
label: t('settings.advanced.title'),
|
label: t('settings.advanced.title')
|
||||||
icon: <SettingOutlined />
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -140,7 +138,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
<div className="settings-label">
|
<div className="settings-label">
|
||||||
{t('settings.tool.preprocess.title')} / {t('settings.tool.ocr.title')}
|
{t('settings.tool.preprocess.title')} / {t('settings.tool.ocr.title')}
|
||||||
<Tooltip title={t('settings.tool.preprocessOrOcr.tooltip')} placement="right">
|
<Tooltip title={t('settings.tool.preprocessOrOcr.tooltip')} placement="right">
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<Select
|
<Select
|
||||||
@ -178,7 +176,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
<div className="settings-label">
|
<div className="settings-label">
|
||||||
{t('models.embedding_model')}
|
{t('models.embedding_model')}
|
||||||
<Tooltip title={t('models.embedding_model_tooltip')} placement="right">
|
<Tooltip title={t('models.embedding_model_tooltip')} placement="right">
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<Select
|
<Select
|
||||||
@ -194,7 +192,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
<div className="settings-label">
|
<div className="settings-label">
|
||||||
{t('models.rerank_model')}
|
{t('models.rerank_model')}
|
||||||
<Tooltip title={t('models.rerank_model_tooltip')} placement="right">
|
<Tooltip title={t('models.rerank_model_tooltip')} placement="right">
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<Select
|
<Select
|
||||||
@ -216,7 +214,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
<div className="settings-label">
|
<div className="settings-label">
|
||||||
{t('knowledge.document_count')}
|
{t('knowledge.document_count')}
|
||||||
<Tooltip title={t('knowledge.document_count_help')}>
|
<Tooltip title={t('knowledge.document_count_help')}>
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<Slider
|
<Slider
|
||||||
@ -239,7 +237,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
<div className="settings-label">
|
<div className="settings-label">
|
||||||
{t('knowledge.chunk_size')}
|
{t('knowledge.chunk_size')}
|
||||||
<Tooltip title={t('knowledge.chunk_size_tooltip')} placement="right">
|
<Tooltip title={t('knowledge.chunk_size_tooltip')} placement="right">
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
@ -260,7 +258,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
<div className="settings-label">
|
<div className="settings-label">
|
||||||
{t('knowledge.chunk_overlap')}
|
{t('knowledge.chunk_overlap')}
|
||||||
<Tooltip title={t('knowledge.chunk_overlap_tooltip')} placement="right">
|
<Tooltip title={t('knowledge.chunk_overlap_tooltip')} placement="right">
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
@ -281,7 +279,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
<div className="settings-label">
|
<div className="settings-label">
|
||||||
{t('knowledge.threshold')}
|
{t('knowledge.threshold')}
|
||||||
<Tooltip title={t('knowledge.threshold_tooltip')} placement="right">
|
<Tooltip title={t('knowledge.threshold_tooltip')} placement="right">
|
||||||
<InfoCircleOutlined style={{ marginLeft: 8 }} />
|
<InfoCircleOutlined style={{ marginLeft: 8, color: 'var(--color-text-3)' }} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<InputNumber
|
<InputNumber
|
||||||
@ -319,9 +317,10 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
destroyOnClose
|
destroyOnClose
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
centered
|
centered
|
||||||
|
transitionName="animation-move-down"
|
||||||
width="min(800px, 70vw)"
|
width="min(800px, 70vw)"
|
||||||
styles={{
|
styles={{
|
||||||
body: { padding: 0, height: '50vh' },
|
body: { padding: 0, height: 450 },
|
||||||
header: {
|
header: {
|
||||||
padding: '10px 15px',
|
padding: '10px 15px',
|
||||||
borderBottom: '0.5px solid var(--color-border)',
|
borderBottom: '0.5px solid var(--color-border)',
|
||||||
@ -330,10 +329,11 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
|||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
|
paddingBottom: 10,
|
||||||
overflow: 'hidden'
|
overflow: 'hidden'
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
<HStack>
|
<HStack height="100%">
|
||||||
<LeftMenu>
|
<LeftMenu>
|
||||||
<StyledMenu
|
<StyledMenu
|
||||||
defaultSelectedKeys={['general']}
|
defaultSelectedKeys={['general']}
|
||||||
@ -398,19 +398,19 @@ const SettingsModal = styled(Modal)`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const LeftMenu = styled.div`
|
const LeftMenu = styled.div`
|
||||||
height: 50vh;
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
border-right: 0.5px solid var(--color-border);
|
border-right: 0.5px solid var(--color-border);
|
||||||
`
|
`
|
||||||
|
|
||||||
const SettingsContentPanel = styled.div`
|
const SettingsContentPanel = styled.div`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 16px 16px;
|
padding: 16px 16px;
|
||||||
height: calc(50vh - 16px);
|
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
`
|
`
|
||||||
|
|
||||||
const StyledMenu = styled(Menu)`
|
const StyledMenu = styled(Menu)`
|
||||||
width: 220px;
|
width: 200px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user