feat(OCR设置): 添加OCR提供商设置组件及状态管理

新增OCR提供商设置组件,支持显示当前选择的OCR提供商信息
在OCR图片设置中添加状态管理,同步提供商选择到父组件
添加Tesseract OCR设置组件,支持多语言选择(暂不可用)
This commit is contained in:
icarus 2025-08-23 23:51:05 +08:00
parent c549fff44a
commit cb362a4e2e
4 changed files with 126 additions and 4 deletions

View File

@ -1,8 +1,9 @@
import { loggerService } from '@logger'
import { useAppSelector } from '@renderer/store'
import { setImageOcrProvider } from '@renderer/store/ocr'
import { isImageOcrProvider } from '@renderer/types'
import { isImageOcrProvider, OcrProvider } from '@renderer/types'
import { Select } from 'antd'
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch } from 'react-redux'
@ -10,13 +11,22 @@ import { SettingRow, SettingRowTitle } from '..'
const logger = loggerService.withContext('OcrImageSettings')
const OcrImageSettings = () => {
type Props = {
setProvider: (provider: OcrProvider) => void
}
const OcrImageSettings = ({ setProvider }: Props) => {
const { t } = useTranslation()
const providers = useAppSelector((state) => state.ocr.providers)
const imageProvider = useAppSelector((state) => state.ocr.imageProvider)
const imageProviders = providers.filter((p) => isImageOcrProvider(p))
const dispatch = useDispatch()
// 挂载时更新外部状态
useEffect(() => {
setProvider(imageProvider)
}, [imageProvider, setProvider])
const updateImageProvider = (id: string) => {
const provider = imageProviders.find((p) => p.id === id)
if (!provider) {
@ -25,6 +35,7 @@ const OcrImageSettings = () => {
return
}
setProvider(provider)
dispatch(setImageOcrProvider(provider))
}

View File

@ -0,0 +1,51 @@
// import { loggerService } from '@logger'
import { isBuiltinOcrProvider, OcrProvider } from '@renderer/types'
import { getOcrProviderLogo } from '@renderer/utils/ocr'
import { Avatar, Divider, Flex } from 'antd'
import styled from 'styled-components'
import { SettingTitle } from '..'
import { OcrTesseractSettings } from './OcrTesseractSettings'
// const logger = loggerService.withContext('OcrTesseractSettings')
type Props = {
provider: OcrProvider
}
const OcrProviderSettings = ({ provider }: Props) => {
// const { t } = useTranslation()
const getProviderSettings = () => {
if (isBuiltinOcrProvider(provider)) {
switch (provider.id) {
case 'tesseract':
return <OcrTesseractSettings />
}
} else {
throw new Error('Not supported OCR provider')
}
}
return (
<>
<SettingTitle>
<Flex align="center" gap={8}>
<ProviderLogo shape="square" src={getOcrProviderLogo(provider.id)} size={16} />
<ProviderName> {provider.name}</ProviderName>
</Flex>
</SettingTitle>
<Divider style={{ width: '100%', margin: '10px 0' }} />
{getProviderSettings()}
</>
)
}
const ProviderName = styled.span`
font-size: 14px;
font-weight: 500;
`
const ProviderLogo = styled(Avatar)`
border: 0.5px solid var(--color-border);
`
export default OcrProviderSettings

View File

@ -1,22 +1,27 @@
import { PictureOutlined } from '@ant-design/icons'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useAppSelector } from '@renderer/store'
import { OcrProvider } from '@renderer/types'
import { Tabs, TabsProps } from 'antd'
import { FC } from 'react'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SettingDivider, SettingGroup, SettingTitle } from '..'
import OcrImageSettings from './OcrImageSettings'
import OcrProviderSettings from './OcrProviderSettings'
const OcrSettings: FC = () => {
const { t } = useTranslation()
const { theme: themeMode } = useTheme()
const imageProvider = useAppSelector((state) => state.ocr.imageProvider)
const [provider, setProvider] = useState<OcrProvider>(imageProvider) // since default to image provider
const tabs: TabsProps['items'] = [
{
key: 'image',
label: t('settings.tool.ocr.image.title'),
icon: <PictureOutlined />,
children: <OcrImageSettings />
children: <OcrImageSettings setProvider={setProvider} />
}
]
@ -27,6 +32,9 @@ const OcrSettings: FC = () => {
<SettingDivider />
<Tabs defaultActiveKey="image" items={tabs} />
</SettingGroup>
<SettingGroup theme={themeMode}>
<OcrProviderSettings provider={provider} />
</SettingGroup>
</>
)
}

View File

@ -0,0 +1,52 @@
// import { loggerService } from '@logger'
import InfoTooltip from '@renderer/components/InfoTooltip'
import { useOcrProvider } from '@renderer/hooks/useOcrProvider'
import { BuiltinOcrProviderIds, isOcrTesseractProvider } from '@renderer/types'
import { Flex, Select } from 'antd'
import { useTranslation } from 'react-i18next'
import { SettingRow, SettingRowTitle } from '..'
// const logger = loggerService.withContext('OcrTesseractSettings')
export const OcrTesseractSettings = () => {
const { t } = useTranslation()
const { provider } = useOcrProvider(BuiltinOcrProviderIds.tesseract)
// TODO: use error boundary
if (!isOcrTesseractProvider(provider)) {
throw new Error('Not tesseract provider.')
}
// const [langs, setLangs] = useState<OcrTesseractConfig['langs']>(provider.config?.langs ?? {})
// currently static
const options = [
{ value: 'chi_sim', label: t('languages.chinese') },
{ value: 'chi_tra', label: t('languages.chinese-traditional') },
{ value: 'eng', label: t('languages.english') }
]
return (
<>
<SettingRow>
<SettingRowTitle>
<Flex align="center" gap={4}>
{t('settings.tool.ocr.image.tesseract.langs')}
<InfoTooltip title={t('settings.tool.ocr.image.tesseract.temp_tooltip')} />
</Flex>
</SettingRowTitle>
<div style={{ display: 'flex', gap: '8px' }}>
<Select
mode="multiple"
disabled
style={{ width: '100%' }}
placeholder="Please select"
defaultValue={'chi_sim'}
options={options}
/>
</div>
</SettingRow>
</>
)
}