refactor(Settings): Refactored knowledge settings to utilize the new OCR provider management.
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
const OcrIcon: FC<React.SVGProps<SVGSVGElement>> = (props) => (
|
||||
<svg
|
||||
className="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="3083"
|
||||
width="16"
|
||||
height="16"
|
||||
strokeWidth="1"
|
||||
{...props}>
|
||||
<path
|
||||
d="M640.189 960.294l-0.131-64 254.651-0.522 1.285-255.968 64 0.322-1.605 319.515-318.2 0.653z m-256.239-0.075l-318.197-1.237 0.251-319.042 64 0.051-0.201 255.239 254.396 0.989-0.249 64zM66.004 383.837L65.53 64.399l318.728 1.829-0.367 63.999-254.265-1.459 0.378 254.975-64 0.094zM897.495 383l-0.661-252.989-254.683 0.217-0.055-64 318.569-0.271 0.829 316.876-63.999 0.167zM404.866 510.522c0 24.75-5.328 46.895-15.984 66.43-10.656 19.537-25.553 34.719-44.688 45.547-19.137 10.828-40.562 16.242-64.281 16.242-23.146 0-44.201-5.242-63.164-15.727-18.965-10.484-33.717-25.207-44.258-44.172-10.543-18.963-15.812-40.418-15.812-64.367 0-25.093 5.328-47.665 15.984-67.718 10.656-20.05 25.609-35.549 44.859-46.492 19.25-10.941 41.135-16.414 65.656-16.414 23.604 0 44.714 5.242 63.336 15.727 18.619 10.484 33 25.438 43.141 44.859s15.211 41.451 15.211 66.085z m-78.719 2.062c0-20.281-3.896-36.265-11.688-47.953-7.793-11.688-18.45-17.531-31.969-17.531-14.781 0-26.297 5.615-34.547 16.844-8.25 11.231-12.375 27.1-12.375 47.609 0 20.053 4.096 35.693 12.289 46.922 8.191 11.23 19.336 16.844 33.43 16.844 8.594 0 16.328-2.52 23.203-7.562 6.875-5.041 12.203-12.26 15.984-21.656 3.782-9.396 5.673-20.566 5.673-33.517zM623.147 627.741c-20.396 7.332-43.943 11-70.641 11-26.24 0-48.872-5.012-67.891-15.039-19.021-10.025-33.545-24.291-43.57-42.797-10.028-18.504-15.039-39.846-15.039-64.023 0-26.009 5.613-49.156 16.844-69.437 11.229-20.281 27.097-35.949 47.609-47.008 20.51-11.057 44.114-16.586 70.813-16.586 21.312 0 41.938 2.578 61.875 7.734v68.578c-6.875-4.125-15.068-7.332-24.578-9.625a122.892 122.892 0 0 0-28.875-3.438c-20.168 0-36.066 5.787-47.695 17.359-11.631 11.575-17.446 27.271-17.446 47.093 0 19.709 5.814 35.264 17.446 46.664 11.629 11.402 27.184 17.102 46.664 17.102 17.988 0 36.15-4.582 54.484-13.75v66.173zM789.351 634.444l-18.391-53.109c-3.553-10.426-8.164-18.619-13.836-24.578-5.672-5.957-11.832-8.938-18.477-8.938h-2.922v86.625h-74.25V387.975h98.656c34.488 0 59.955 5.645 76.398 16.93 16.441 11.287 24.664 28.217 24.664 50.789 0 16.959-4.785 31.168-14.352 42.625-9.568 11.458-23.805 19.765-42.711 24.921v0.688c10.426 3.209 19.105 8.422 26.039 15.641 6.932 7.219 13.148 17.934 18.648 32.141l24.234 62.734h-83.7z m-7.047-168.265c0-8.25-2.521-14.781-7.562-19.594-5.043-4.812-12.949-7.219-23.719-7.219h-15.297v56.375h13.406c9.969 0 17.988-2.807 24.062-8.422 6.073-5.613 9.11-12.659 9.11-21.14z"
|
||||
p-id="3084"></path>
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default OcrIcon
|
||||
@@ -0,0 +1,16 @@
|
||||
import { FC } from 'react'
|
||||
|
||||
const ToolIcon: FC<React.SVGProps<SVGSVGElement>> = (props) => (
|
||||
<svg
|
||||
viewBox="150 150 724 724"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
className="icon"
|
||||
{...props}>
|
||||
<path d="M732.16 450.048h-12.288V377.856c0-37.888-31.744-69.12-70.656-69.12h-73.216c1.024-7.168 1.024-14.848 1.024-22.016 0-75.776-62.976-137.728-140.288-137.728S296.448 210.944 296.448 286.72c0 7.168 0 14.848 1.024 22.016H224.256c-38.912 0-70.656 31.232-70.656 69.12v102.4c0 9.216 3.584 15.872 10.752 21.504 7.168 5.632 14.848 7.168 23.552 5.632 7.68-1.536 16.384-3.072 22.528-3.072 46.08 0 84.992 38.4 84.992 83.456 0 46.08-37.888 83.456-84.992 83.456-6.656 0-14.848-1.536-22.528-3.072-7.68-2.048-16.896 0.512-23.552 5.632-7.168 5.12-10.752 12.288-10.752 21.504v102.4c0 37.888 31.744 69.12 70.656 69.12H650.24c19.456 0 38.4-8.192 51.712-22.016 12.288-13.312 18.432-29.696 17.92-47.104v-72.192h12.288c77.312 0 140.288-61.952 140.288-137.728 0-75.776-62.976-137.728-140.288-137.728z m-66.048 239.616v105.984c0 8.192-7.168 15.36-15.872 15.36H224.768c-8.704 0-15.872-6.656-15.872-15.36v-70.656c0.512 0 1.536 0 2.048-0.512 77.312-0.512 139.776-61.952 139.776-137.728 0-37.376-14.848-72.704-41.984-98.816-27.136-26.112-62.976-39.936-100.352-38.912V378.368c0-8.192 7.168-15.36 15.872-15.36h110.08c8.704 0 17.92-4.608 23.552-12.288 4.608-6.656 5.632-15.36 2.048-25.088-5.12-11.264-8.192-24.064-8.192-36.352 0-46.08 37.888-83.456 84.992-83.456 46.08 0 84.992 38.4 84.992 83.456 0 10.752-1.024 23.04-7.68 35.84-4.608 7.68-3.584 18.432 1.536 25.6 5.12 6.144 13.824 12.288 23.552 12.288H650.24c8.704 0 15.872 6.656 15.872 15.36v107.52c0 9.216 3.584 15.872 10.752 21.504 7.68 5.632 16.896 6.656 25.6 3.072 8.192-4.096 17.408-6.144 28.672-6.144 46.08 0 84.992 38.4 84.992 83.456 0 46.08-37.888 83.456-84.992 83.456-8.192 0-17.408-3.072-26.112-5.632l-3.072-1.024c-10.24-2.56-18.432-1.536-25.088 3.584-6.656 3.072-10.752 11.264-10.752 21.504z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default ToolIcon
|
||||
@@ -17,11 +17,9 @@ import {
|
||||
updateBases,
|
||||
updateItem as updateItemAction,
|
||||
updateItemProcessingStatus,
|
||||
updateNotes,
|
||||
updateOcrProvider as _updateOcrProvider,
|
||||
updateOcrProviders as _updateOcrProviders
|
||||
updateNotes
|
||||
} from '@renderer/store/knowledge'
|
||||
import { FileType, KnowledgeBase, KnowledgeItem, OcrProvider, ProcessingStatus } from '@renderer/types'
|
||||
import { FileType, KnowledgeBase, KnowledgeItem, ProcessingStatus } from '@renderer/types'
|
||||
import { runAsyncFunction } from '@renderer/utils'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
@@ -394,31 +392,3 @@ export const useKnowledgeBases = () => {
|
||||
updateKnowledgeBases
|
||||
}
|
||||
}
|
||||
|
||||
export const useOcrProviders = () => {
|
||||
const dispatch = useDispatch()
|
||||
const ocrProviders = useSelector((state: RootState) => state.knowledge.ocrProviders)
|
||||
const updateOcrProviders = (ocrProviders: OcrProvider[]) => {
|
||||
dispatch(_updateOcrProviders(ocrProviders))
|
||||
}
|
||||
return {
|
||||
ocrProviders,
|
||||
updateOcrProviders
|
||||
}
|
||||
}
|
||||
export const useOcrProvider = (id: string) => {
|
||||
const dispatch = useDispatch()
|
||||
const ocrProviders = useSelector((state: RootState) => state.knowledge.ocrProviders)
|
||||
const ocrProvider = ocrProviders.find((provider) => provider.id === id)
|
||||
if (!ocrProvider) {
|
||||
throw new Error(`ocr provider with id ${id} not found`)
|
||||
}
|
||||
|
||||
const updateOcrProvider = (ocrProvider: OcrProvider) => {
|
||||
dispatch(_updateOcrProvider(ocrProvider))
|
||||
}
|
||||
return {
|
||||
ocrProvider,
|
||||
updateOcrProvider
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { RootState } from '@renderer/store'
|
||||
import {
|
||||
setDefaultOcrProvider as _setDefaultOcrProvider,
|
||||
updateOcrProvider as _updateOcrProvider,
|
||||
updateOcrProviders as _updateOcrProviders
|
||||
} from '@renderer/store/ocr'
|
||||
import { OcrProvider } from '@renderer/types'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
|
||||
export const useOcrProvider = (id: string) => {
|
||||
const dispatch = useDispatch()
|
||||
const ocrProviders = useSelector((state: RootState) => state.ocr.providers)
|
||||
const provider = ocrProviders.find((provider) => provider.id === id)
|
||||
if (!provider) {
|
||||
throw new Error(`ocr provider with id ${id} not found`)
|
||||
}
|
||||
const updateOcrProvider = (ocrProvider: OcrProvider) => {
|
||||
dispatch(_updateOcrProvider(ocrProvider))
|
||||
}
|
||||
return { provider, updateOcrProvider }
|
||||
}
|
||||
|
||||
export const useOcrProviders = () => {
|
||||
const dispatch = useDispatch()
|
||||
const ocrProviders = useSelector((state: RootState) => state.ocr.providers)
|
||||
return {
|
||||
ocrProviders,
|
||||
updateOcrProviders: (ocrProviders: OcrProvider[]) => dispatch(_updateOcrProviders(ocrProviders))
|
||||
}
|
||||
}
|
||||
|
||||
export const useDefaultOcrProvider = () => {
|
||||
const defaultProviderId = useSelector((state: RootState) => state.ocr.defaultProvider)
|
||||
const { ocrProviders } = useOcrProviders()
|
||||
const dispatch = useDispatch()
|
||||
const provider = defaultProviderId ? ocrProviders.find((provider) => provider.id === defaultProviderId) : undefined
|
||||
|
||||
const setDefaultOcrProvider = (ocrProvider: OcrProvider) => {
|
||||
dispatch(_setDefaultOcrProvider(ocrProvider.id))
|
||||
}
|
||||
const updateDefaultOcrProvider = (ocrProvider: OcrProvider) => {
|
||||
dispatch(_updateOcrProvider(ocrProvider))
|
||||
}
|
||||
return { provider, setDefaultOcrProvider, updateDefaultOcrProvider }
|
||||
}
|
||||
@@ -369,7 +369,11 @@
|
||||
"rename": "Rename",
|
||||
"search": "Search knowledge base",
|
||||
"search_placeholder": "Enter text to search",
|
||||
"settings": "Knowledge Base Settings",
|
||||
"settings": {
|
||||
"title": "Knowledge Base Settings",
|
||||
"preprocessing": "Preprocessing",
|
||||
"preprocessing_tooltip": "Preprocess uploaded files with OCR"
|
||||
},
|
||||
"sitemap_placeholder": "Enter Website Map URL",
|
||||
"sitemaps": "Websites",
|
||||
"source": "Source",
|
||||
@@ -1101,37 +1105,46 @@
|
||||
"theme.window.style.title": "Window Style",
|
||||
"theme.window.style.transparent": "Transparent Window",
|
||||
"title": "Settings",
|
||||
"tool": {
|
||||
"websearch": {
|
||||
"blacklist": "Blacklist",
|
||||
"blacklist_description": "Results from the following websites will not appear in search results",
|
||||
"blacklist_tooltip": "Please use the following format (separated by line breaks)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "Check",
|
||||
"check_failed": "Verification failed",
|
||||
"check_success": "Verification successful",
|
||||
"enhance_mode": "Search enhance mode",
|
||||
"enhance_mode_tooltip": "Use the default model to extract search keywords from the problem and search",
|
||||
"get_api_key": "Get API Key",
|
||||
"no_provider_selected": "Please select a search service provider before checking.",
|
||||
"search_max_result": "Number of search results",
|
||||
"search_provider": "Search service provider",
|
||||
"search_provider_placeholder": "Choose a search service provider.",
|
||||
"search_result_default": "Default",
|
||||
"search_with_time": "Search with dates included",
|
||||
"tavily": {
|
||||
"api_key": "Tavily API Key",
|
||||
"api_key.placeholder": "Enter Tavily API Key",
|
||||
"description": "Tavily is a search engine tailored for AI agents, delivering real-time, accurate results, intelligent query suggestions, and in-depth research capabilities.",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "Web Search"
|
||||
},
|
||||
"ocr": {
|
||||
"title": "OCR",
|
||||
"provider": "OCR Provider",
|
||||
"ocr": "OCR Settings",
|
||||
"provider_placeholder": "Choose a OCR provider"
|
||||
},
|
||||
"title": "Tool Settings"
|
||||
},
|
||||
"topic.position": "Topic position",
|
||||
"topic.position.left": "Left",
|
||||
"topic.position.right": "Right",
|
||||
"topic.show.time": "Show topic time",
|
||||
"tray.onclose": "Minimize to Tray on Close",
|
||||
"tray.show": "Show Tray Icon",
|
||||
"tray.title": "Tray",
|
||||
"websearch": {
|
||||
"blacklist": "Blacklist",
|
||||
"blacklist_description": "Results from the following websites will not appear in search results",
|
||||
"blacklist_tooltip": "Please use the following format (separated by line breaks)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "Check",
|
||||
"check_failed": "Verification failed",
|
||||
"check_success": "Verification successful",
|
||||
"enhance_mode": "Search enhance mode",
|
||||
"enhance_mode_tooltip": "Use the default model to extract search keywords from the problem and search",
|
||||
"get_api_key": "Get API Key",
|
||||
"no_provider_selected": "Please select a search service provider before checking.",
|
||||
"search_max_result": "Number of search results",
|
||||
"search_provider": "Search service provider",
|
||||
"search_provider_placeholder": "Choose a search service provider.",
|
||||
"search_result_default": "Default",
|
||||
"search_with_time": "Search with dates included",
|
||||
"tavily": {
|
||||
"api_key": "Tavily API Key",
|
||||
"api_key.placeholder": "Enter Tavily API Key",
|
||||
"description": "Tavily is a search engine tailored for AI agents, delivering real-time, accurate results, intelligent query suggestions, and in-depth research capabilities.",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "Web Search"
|
||||
}
|
||||
"tray.title": "Tray"
|
||||
},
|
||||
"translate": {
|
||||
"any.language": "Any language",
|
||||
|
||||
@@ -369,7 +369,11 @@
|
||||
"rename": "名前を変更",
|
||||
"search": "ナレッジベースを検索",
|
||||
"search_placeholder": "検索するテキストを入力",
|
||||
"settings": "ナレッジベース設定",
|
||||
"settings": {
|
||||
"title": "ナレッジベース設定",
|
||||
"preprocessing": "預処理",
|
||||
"preprocessing_tooltip": "アップロードされたファイルのOCR預処理"
|
||||
},
|
||||
"sitemap_placeholder": "サイトマップURLを入力",
|
||||
"sitemaps": "サイトマップ",
|
||||
"source": "ソース",
|
||||
@@ -1101,37 +1105,47 @@
|
||||
"theme.window.style.title": "ウィンドウスタイル",
|
||||
"theme.window.style.transparent": "透明ウィンドウ",
|
||||
"title": "設定",
|
||||
"tool": {
|
||||
"title": "ツール設定",
|
||||
"ocr": {
|
||||
"title": "ナレッジベース設定",
|
||||
"preprocessing": "預処理",
|
||||
"preprocessing_tooltip": "アップロードされたファイルのOCR預処理",
|
||||
"provider": "OCRプロバイダー",
|
||||
"provider_placeholder": "OCRプロバイダーを選択する"
|
||||
},
|
||||
"websearch": {
|
||||
"blacklist": "ブラックリスト",
|
||||
"blacklist_description": "以下のウェブサイトの結果は検索結果に表示されません",
|
||||
"blacklist_tooltip": "以下の形式を使用してください(改行区切り)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "チェック",
|
||||
"check_failed": "検証に失敗しました",
|
||||
"check_success": "検証に成功しました",
|
||||
"enhance_mode": "検索強化モード",
|
||||
"enhance_mode_tooltip": "デフォルトモデルを使用して問題から検索キーワードを抽出し、検索を実行します",
|
||||
"get_api_key": "APIキーを取得",
|
||||
"no_provider_selected": "検索サービスプロバイダーを選択してから再確認してください。",
|
||||
"search_max_result": "検索結果の数",
|
||||
"search_provider": "検索サービスプロバイダー",
|
||||
"search_provider_placeholder": "検索サービスプロバイダーを選択する",
|
||||
"search_result_default": "デフォルト",
|
||||
"search_with_time": "日付を含む検索",
|
||||
"tavily": {
|
||||
"api_key": "Tavily API キー",
|
||||
"api_key.placeholder": "Tavily API キーを入力してください",
|
||||
"description": "Tavily は、AI エージェントのために特別に開発された検索エンジンで、最新の結果、インテリジェントな検索提案、そして深い研究能力を提供します",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "ウェブ検索"
|
||||
}
|
||||
},
|
||||
"topic.position": "トピックの位置",
|
||||
"topic.position.left": "左",
|
||||
"topic.position.right": "右",
|
||||
"topic.show.time": "トピックの時間を表示",
|
||||
"tray.onclose": "閉じるときにトレイに最小化",
|
||||
"tray.show": "トレイアイコンを表示",
|
||||
"tray.title": "トレイ",
|
||||
"websearch": {
|
||||
"blacklist": "ブラックリスト",
|
||||
"blacklist_description": "以下のウェブサイトの結果は検索結果に表示されません",
|
||||
"blacklist_tooltip": "以下の形式を使用してください(改行区切り)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "チェック",
|
||||
"check_failed": "検証に失敗しました",
|
||||
"check_success": "検証に成功しました",
|
||||
"enhance_mode": "検索強化モード",
|
||||
"enhance_mode_tooltip": "デフォルトモデルを使用して問題から検索キーワードを抽出し、検索を実行します",
|
||||
"get_api_key": "APIキーを取得",
|
||||
"no_provider_selected": "検索サービスプロバイダーを選択してから再確認してください。",
|
||||
"search_max_result": "検索結果の数",
|
||||
"search_provider": "検索サービスプロバイダー",
|
||||
"search_provider_placeholder": "検索サービスプロバイダーを選択する",
|
||||
"search_result_default": "デフォルト",
|
||||
"search_with_time": "日付を含む検索",
|
||||
"tavily": {
|
||||
"api_key": "Tavily API キー",
|
||||
"api_key.placeholder": "Tavily API キーを入力してください",
|
||||
"description": "Tavily は、AI エージェントのために特別に開発された検索エンジンで、最新の結果、インテリジェントな検索提案、そして深い研究能力を提供します",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "ウェブ検索"
|
||||
}
|
||||
"tray.title": "トレイ"
|
||||
},
|
||||
"translate": {
|
||||
"any.language": "任意の言語",
|
||||
|
||||
@@ -369,7 +369,11 @@
|
||||
"rename": "Переименовать",
|
||||
"search": "Поиск в базе знаний",
|
||||
"search_placeholder": "Введите текст для поиска",
|
||||
"settings": "Настройки базы знаний",
|
||||
"settings": {
|
||||
"title": "Настройки базы знаний",
|
||||
"preprocessing": "Предварительная обработка",
|
||||
"preprocessing_tooltip": "Предварительная обработка изображений с помощью OCR"
|
||||
},
|
||||
"sitemap_placeholder": "Введите URL карты сайта",
|
||||
"sitemaps": "Сайты",
|
||||
"source": "Источник",
|
||||
@@ -1101,37 +1105,45 @@
|
||||
"theme.window.style.title": "Стиль окна",
|
||||
"theme.window.style.transparent": "Прозрачное окно",
|
||||
"title": "Настройки",
|
||||
"tool": {
|
||||
"title": "Настройки инструментов",
|
||||
"ocr": {
|
||||
"title": "Настройки OCR",
|
||||
"provider": "Поставщик OCR",
|
||||
"provider_placeholder": "Выберите поставщика OCR"
|
||||
},
|
||||
"websearch": {
|
||||
"blacklist": "Черный список",
|
||||
"blacklist_description": "Результаты из следующих веб-сайтов не будут отображаться в результатах поиска",
|
||||
"blacklist_tooltip": "Пожалуйста, используйте следующий формат (разделенный переносами строк)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "проверка",
|
||||
"check_failed": "Проверка не прошла",
|
||||
"check_success": "Проверка успешна",
|
||||
"enhance_mode": "Режим улучшения поиска",
|
||||
"enhance_mode_tooltip": "Используйте модель по умолчанию для извлечения ключевых слов из проблемы и поиска",
|
||||
"get_api_key": "Получить ключ API",
|
||||
"no_provider_selected": "Пожалуйста, выберите поставщика поисковых услуг, затем проверьте.",
|
||||
"search_max_result": "Количество результатов поиска",
|
||||
"search_provider": "поиск сервисного провайдера",
|
||||
"search_provider_placeholder": "Выберите поставщика поисковых услуг",
|
||||
"search_result_default": "По умолчанию",
|
||||
"search_with_time": "Поиск, содержащий дату",
|
||||
"tavily": {
|
||||
"api_key": "Ключ API Tavily",
|
||||
"api_key.placeholder": "Введите ключ API Tavily",
|
||||
"description": "Tavily — это поисковая система, специально разработанная для ИИ-агентов, предоставляющая актуальные результаты, умные предложения по запросам и глубокие исследовательские возможности",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "Поиск в Интернете"
|
||||
}
|
||||
},
|
||||
"topic.position": "Позиция топиков",
|
||||
"topic.position.left": "Слева",
|
||||
"topic.position.right": "Справа",
|
||||
"topic.show.time": "Показывать время топика",
|
||||
"tray.onclose": "Свернуть в трей при закрытии",
|
||||
"tray.show": "Показать значок в трее",
|
||||
"tray.title": "Трей",
|
||||
"websearch": {
|
||||
"blacklist": "Черный список",
|
||||
"blacklist_description": "Результаты из следующих веб-сайтов не будут отображаться в результатах поиска",
|
||||
"blacklist_tooltip": "Пожалуйста, используйте следующий формат (разделенный переносами строк)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "проверка",
|
||||
"check_failed": "Проверка не прошла",
|
||||
"check_success": "Проверка успешна",
|
||||
"enhance_mode": "Режим улучшения поиска",
|
||||
"enhance_mode_tooltip": "Используйте модель по умолчанию для извлечения ключевых слов из проблемы и поиска",
|
||||
"get_api_key": "Получить ключ API",
|
||||
"no_provider_selected": "Пожалуйста, выберите поставщика поисковых услуг, затем проверьте.",
|
||||
"search_max_result": "Количество результатов поиска",
|
||||
"search_provider": "поиск сервисного провайдера",
|
||||
"search_provider_placeholder": "Выберите поставщика поисковых услуг",
|
||||
"search_result_default": "По умолчанию",
|
||||
"search_with_time": "Поиск, содержащий дату",
|
||||
"tavily": {
|
||||
"api_key": "Ключ API Tavily",
|
||||
"api_key.placeholder": "Введите ключ API Tavily",
|
||||
"description": "Tavily — это поисковая система, специально разработанная для ИИ-агентов, предоставляющая актуальные результаты, умные предложения по запросам и глубокие исследовательские возможности",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "Поиск в Интернете"
|
||||
}
|
||||
"tray.title": "Трей"
|
||||
},
|
||||
"translate": {
|
||||
"any.language": "Любой язык",
|
||||
|
||||
@@ -369,6 +369,7 @@
|
||||
"rename": "重命名",
|
||||
"search": "搜索知识库",
|
||||
"search_placeholder": "输入查询内容",
|
||||
"settings": "知识库设置",
|
||||
"sitemap_placeholder": "请输入站点地图 URL",
|
||||
"sitemaps": "网站",
|
||||
"source": "来源",
|
||||
@@ -389,18 +390,7 @@
|
||||
"topN_tooltip": "返回的匹配结果数量,数值越大,匹配结果越多,但消耗的 Token 也越多",
|
||||
"url_added": "网址已添加",
|
||||
"url_placeholder": "请输入网址, 多个网址用回车分隔",
|
||||
"urls": "网址",
|
||||
"settings": {
|
||||
"title": "知识库设置",
|
||||
"preprocessing": "预处理",
|
||||
"preprocessing_tooltip": "对上传的文件进行ocr预处理",
|
||||
"general": "常规设置",
|
||||
"ocr": "OCR 设置",
|
||||
"advanced": "高级设置",
|
||||
"doc2x_api_key": "Doc2X API Key",
|
||||
"doc2x_api_key_placeholder": "请输入 Doc2X API Key",
|
||||
"doc2x_api_key_tooltip": "使用doc2x进行文档预处理"
|
||||
}
|
||||
"urls": "网址"
|
||||
},
|
||||
"languages": {
|
||||
"arabic": "阿拉伯文",
|
||||
@@ -1118,37 +1108,45 @@
|
||||
"theme.window.style.title": "窗口样式",
|
||||
"theme.window.style.transparent": "透明窗口",
|
||||
"title": "设置",
|
||||
"tool": {
|
||||
"title": "工具设置",
|
||||
"ocr": {
|
||||
"title": "知识库设置",
|
||||
"provider": "OCR服务商",
|
||||
"provider_placeholder": "选择一个OCR服务商"
|
||||
},
|
||||
"websearch": {
|
||||
"blacklist": "黑名单",
|
||||
"blacklist_description": "在搜索结果中不会出现以下网站的结果",
|
||||
"blacklist_tooltip": "请使用以下格式(换行分隔)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "检查",
|
||||
"check_failed": "验证失败",
|
||||
"check_success": "验证成功",
|
||||
"enhance_mode": "搜索增强模式",
|
||||
"enhance_mode_tooltip": "使用默认模型提取关键词后搜索",
|
||||
"get_api_key": "点击这里获取密钥",
|
||||
"no_provider_selected": "请选择搜索服务商后再检查",
|
||||
"search_max_result": "搜索结果个数",
|
||||
"search_provider": "搜索服务商",
|
||||
"search_provider_placeholder": "选择一个搜索服务商",
|
||||
"search_result_default": "默认",
|
||||
"search_with_time": "搜索包含日期",
|
||||
"tavily": {
|
||||
"api_key": "Tavily API 密钥",
|
||||
"api_key.placeholder": "请输入 Tavily API 密钥",
|
||||
"description": "Tavily 是一个为 AI 代理量身定制的搜索引擎,提供实时、准确的结果、智能查询建议和深入的研究能力",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "网络搜索"
|
||||
}
|
||||
},
|
||||
"topic.position": "话题位置",
|
||||
"topic.position.left": "左侧",
|
||||
"topic.position.right": "右侧",
|
||||
"topic.show.time": "显示话题时间",
|
||||
"tray.onclose": "关闭时最小化到托盘",
|
||||
"tray.show": "显示托盘图标",
|
||||
"tray.title": "托盘",
|
||||
"websearch": {
|
||||
"blacklist": "黑名单",
|
||||
"blacklist_description": "在搜索结果中不会出现以下网站的结果",
|
||||
"blacklist_tooltip": "请使用以下格式(换行分隔)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "检查",
|
||||
"check_failed": "验证失败",
|
||||
"check_success": "验证成功",
|
||||
"enhance_mode": "搜索增强模式",
|
||||
"enhance_mode_tooltip": "使用默认模型提取关键词后搜索",
|
||||
"get_api_key": "点击这里获取密钥",
|
||||
"no_provider_selected": "请选择搜索服务商后再检查",
|
||||
"search_max_result": "搜索结果个数",
|
||||
"search_provider": "搜索服务商",
|
||||
"search_provider_placeholder": "选择一个搜索服务商",
|
||||
"search_result_default": "默认",
|
||||
"search_with_time": "搜索包含日期",
|
||||
"tavily": {
|
||||
"api_key": "Tavily API 密钥",
|
||||
"api_key.placeholder": "请输入 Tavily API 密钥",
|
||||
"description": "Tavily 是一个为 AI 代理量身定制的搜索引擎,提供实时、准确的结果、智能查询建议和深入的研究能力",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "网络搜索"
|
||||
}
|
||||
"tray.title": "托盘"
|
||||
},
|
||||
"translate": {
|
||||
"any.language": "任意语言",
|
||||
|
||||
@@ -369,7 +369,11 @@
|
||||
"rename": "重新命名",
|
||||
"search": "搜尋知識庫",
|
||||
"search_placeholder": "輸入查詢內容",
|
||||
"settings": "知識庫設定",
|
||||
"settings": {
|
||||
"title": "知識庫設定",
|
||||
"preprocessing": "預處理",
|
||||
"preprocessing_tooltip": "預處理上傳的文件"
|
||||
},
|
||||
"sitemap_placeholder": "請輸入網站地圖 URL",
|
||||
"sitemaps": "網站",
|
||||
"source": "來源",
|
||||
@@ -1101,37 +1105,46 @@
|
||||
"theme.window.style.title": "視窗樣式",
|
||||
"theme.window.style.transparent": "透明視窗",
|
||||
"title": "設定",
|
||||
"tool": {
|
||||
"title": "工具",
|
||||
"ocr": {
|
||||
"title": "OCR",
|
||||
"description": "OCR 設定",
|
||||
"provider": "OCR 提供者",
|
||||
"provider_placeholder": "選擇一個 OCR 提供者"
|
||||
},
|
||||
"websearch": {
|
||||
"blacklist": "黑名單",
|
||||
"blacklist_description": "以下網站不會出現在搜尋結果中",
|
||||
"blacklist_tooltip": "請使用以下格式 (換行符號分隔)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "檢查",
|
||||
"check_failed": "驗證失敗",
|
||||
"check_success": "驗證成功",
|
||||
"enhance_mode": "搜索增強模式",
|
||||
"enhance_mode_tooltip": "使用預設模型提取關鍵詞後搜索",
|
||||
"get_api_key": "點選這裡取得金鑰",
|
||||
"no_provider_selected": "請選擇搜尋服務商後再檢查",
|
||||
"search_max_result": "搜尋結果個數",
|
||||
"search_provider": "搜尋服務商",
|
||||
"search_provider_placeholder": "選擇一個搜尋服務商",
|
||||
"search_result_default": "預設",
|
||||
"search_with_time": "搜尋包含日期",
|
||||
"tavily": {
|
||||
"api_key": "Tavily API 金鑰",
|
||||
"api_key.placeholder": "請輸入 Tavily API 金鑰",
|
||||
"description": "Tavily 是一個為 AI 代理量身訂製的搜尋引擎,提供即時、準確的結果、智慧查詢建議和深入的研究能力",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "網路搜尋"
|
||||
}
|
||||
},
|
||||
"topic.position": "話題位置",
|
||||
"topic.position.left": "左側",
|
||||
"topic.position.right": "右側",
|
||||
"topic.show.time": "顯示話題時間",
|
||||
"tray.onclose": "關閉時最小化到系统匣",
|
||||
"tray.show": "顯示系统匣圖示",
|
||||
"tray.title": "系统匣",
|
||||
"websearch": {
|
||||
"blacklist": "黑名單",
|
||||
"blacklist_description": "以下網站不會出現在搜尋結果中",
|
||||
"blacklist_tooltip": "請使用以下格式 (換行符號分隔)\nexample.com\nhttps://www.example.com\nhttps://example.com\n*://*.example.com",
|
||||
"check": "檢查",
|
||||
"check_failed": "驗證失敗",
|
||||
"check_success": "驗證成功",
|
||||
"enhance_mode": "搜索增強模式",
|
||||
"enhance_mode_tooltip": "使用預設模型提取關鍵詞後搜索",
|
||||
"get_api_key": "點選這裡取得金鑰",
|
||||
"no_provider_selected": "請選擇搜尋服務商後再檢查",
|
||||
"search_max_result": "搜尋結果個數",
|
||||
"search_provider": "搜尋服務商",
|
||||
"search_provider_placeholder": "選擇一個搜尋服務商",
|
||||
"search_result_default": "預設",
|
||||
"search_with_time": "搜尋包含日期",
|
||||
"tavily": {
|
||||
"api_key": "Tavily API 金鑰",
|
||||
"api_key.placeholder": "請輸入 Tavily API 金鑰",
|
||||
"description": "Tavily 是一個為 AI 代理量身訂製的搜尋引擎,提供即時、準確的結果、智慧查詢建議和深入的研究能力",
|
||||
"title": "Tavily"
|
||||
},
|
||||
"title": "網路搜尋"
|
||||
}
|
||||
"tray.title": "系统匣"
|
||||
},
|
||||
"translate": {
|
||||
"any.language": "任意語言",
|
||||
@@ -1170,4 +1183,4 @@
|
||||
"visualization": "視覺化"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import styled from 'styled-components'
|
||||
import KnowledgeSearchPopup from './components/KnowledgeSearchPopup'
|
||||
import KnowledgeSettings from './components/KnowledgeSettings'
|
||||
import StatusIcon from './components/StatusIcon'
|
||||
import KnowledgeSettingsPopup from './components/KnowledgeSettingsPopup'
|
||||
|
||||
const { Dragger } = Upload
|
||||
const { Title } = Typography
|
||||
@@ -463,7 +462,7 @@ const KnowledgeContent: FC<KnowledgeContentProps> = ({ selectedBase }) => {
|
||||
<ModelInfo>
|
||||
<div className="model-header">
|
||||
<label>{t('knowledge.model_info')}</label>
|
||||
<Button icon={<SettingOutlined />} onClick={() => KnowledgeSettingsPopup.show({ base })} size="small" />
|
||||
<Button icon={<SettingOutlined />} onClick={() => KnowledgeSettings.show({ base })} size="small" />
|
||||
</div>
|
||||
|
||||
<div className="model-row">
|
||||
|
||||
@@ -24,7 +24,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import AddKnowledgePopup from './components/AddKnowledgePopup'
|
||||
import KnowledgeSettingsPopup from './components/KnowledgeSettingsPopup'
|
||||
import KnowledgeSettings from './components/KnowledgeSettings'
|
||||
import KnowledgeContent from './KnowledgeContent'
|
||||
|
||||
const KnowledgePage: FC = () => {
|
||||
@@ -64,10 +64,10 @@ const KnowledgePage: FC = () => {
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('knowledge.settings'),
|
||||
label: t('knowledge.settings.title'),
|
||||
key: 'settings',
|
||||
icon: <SettingOutlined />,
|
||||
onClick: () => KnowledgeSettingsPopup.show({ base })
|
||||
onClick: () => KnowledgeSettings.show({ base })
|
||||
},
|
||||
{ type: 'divider' },
|
||||
{
|
||||
|
||||
@@ -3,7 +3,8 @@ import { TopView } from '@renderer/components/TopView'
|
||||
import { DEFAULT_KNOWLEDGE_DOCUMENT_COUNT } from '@renderer/config/constant'
|
||||
import { getEmbeddingMaxContext } from '@renderer/config/embedings'
|
||||
import { isEmbeddingModel, isRerankModel } from '@renderer/config/models'
|
||||
import { useKnowledge, useOcrProviders } from '@renderer/hooks/useKnowledge'
|
||||
import { useKnowledge } from '@renderer/hooks/useKnowledge'
|
||||
import { useOcrProviders } from '@renderer/hooks/useOcr'
|
||||
import { useProviders } from '@renderer/hooks/useProvider'
|
||||
import { SettingRowTitle } from '@renderer/pages/settings'
|
||||
import { getModelUniqId } from '@renderer/services/ModelService'
|
||||
@@ -87,7 +88,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
||||
const settingItems: TabsProps['items'] = [
|
||||
{
|
||||
key: '1',
|
||||
label: t('knowledge.settings.general'),
|
||||
label: t('settings.general'),
|
||||
children: (
|
||||
<SettingsPanel>
|
||||
<SettingsItem>
|
||||
@@ -172,11 +173,11 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: t('knowledge.settings.ocr'),
|
||||
label: t('settings.tool.ocr.title'),
|
||||
children: (
|
||||
<SettingsPanel>
|
||||
<SettingsItem>
|
||||
<SettingRowTitle>{t('settings.knowledge.ocr.provider')}</SettingRowTitle>
|
||||
<SettingRowTitle>{t('settings.tool.ocr.provider')}</SettingRowTitle>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<Select
|
||||
value={selectedProvider?.id}
|
||||
@@ -187,7 +188,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
||||
setSelectedProvider(provider)
|
||||
setNewBase({ ...newBase, ocrProvider: provider })
|
||||
}}
|
||||
placeholder={t('settings.websearch.search_provider_placeholder')}
|
||||
placeholder={t('settings.tool.ocr.provider_placeholder')}
|
||||
options={ocrProviders.filter((p) => p.apiKey !== '').map((p) => ({ value: p.id, label: p.name }))}
|
||||
/>
|
||||
</div>
|
||||
@@ -198,7 +199,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
label: t('knowledge.settings.advanced'),
|
||||
label: t('settings.advanced.title'),
|
||||
children: (
|
||||
<SettingsPanel>
|
||||
<SettingsItem>
|
||||
@@ -336,6 +337,9 @@ const SettingsModal = styled(Modal)`
|
||||
max-height: auto;
|
||||
}
|
||||
}
|
||||
.ant-tabs-tab {
|
||||
padding-inline-start: 0px !important;
|
||||
}
|
||||
`
|
||||
|
||||
export default class KnowledgeSettings {
|
||||
|
||||
@@ -115,7 +115,7 @@ const PopupContainer: React.FC<Props> = ({ base: _base, resolve }) => {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t('knowledge.settings')}
|
||||
title={t('knowledge.settings.title')}
|
||||
open={open}
|
||||
onOk={onOk}
|
||||
onCancel={onCancel}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import {
|
||||
CloudOutlined,
|
||||
CodeOutlined,
|
||||
FileSearchOutlined,
|
||||
GlobalOutlined,
|
||||
InfoCircleOutlined,
|
||||
LayoutOutlined,
|
||||
MacCommandOutlined,
|
||||
@@ -11,6 +9,7 @@ import {
|
||||
SettingOutlined
|
||||
} from '@ant-design/icons'
|
||||
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
||||
import ToolIcon from '@renderer/components/Icons/ToolIcon'
|
||||
import { isLocalAi } from '@renderer/config/env'
|
||||
import ModelSettings from '@renderer/pages/settings/ModelSettings/ModelSettings'
|
||||
import { FC } from 'react'
|
||||
@@ -22,12 +21,11 @@ import AboutSettings from './AboutSettings'
|
||||
import DataSettings from './DataSettings/DataSettings'
|
||||
import DisplaySettings from './DisplaySettings/DisplaySettings'
|
||||
import GeneralSettings from './GeneralSettings'
|
||||
import KnowledgeSettings from './KnowledgeSettings'
|
||||
import MCPSettings from './MCPSettings'
|
||||
import ProvidersList from './ProviderSettings'
|
||||
import QuickAssistantSettings from './QuickAssistantSettings'
|
||||
import ShortcutSettings from './ShortcutSettings'
|
||||
import WebSearchSettings from './WebSearchSettings'
|
||||
import ToolSettings from './ToolSettings'
|
||||
|
||||
const SettingsPage: FC = () => {
|
||||
const { pathname } = useLocation()
|
||||
@@ -58,10 +56,10 @@ const SettingsPage: FC = () => {
|
||||
</MenuItemLink>
|
||||
</>
|
||||
)}
|
||||
<MenuItemLink to="/settings/web-search">
|
||||
<MenuItem className={isRoute('/settings/web-search')}>
|
||||
<GlobalOutlined />
|
||||
{t('settings.websearch.title')}
|
||||
<MenuItemLink to="/settings/tool">
|
||||
<MenuItem className={isRoute('/settings/tool')}>
|
||||
<ToolIcon />
|
||||
{t('settings.tool.title')}
|
||||
</MenuItem>
|
||||
</MenuItemLink>
|
||||
<MenuItemLink to="/settings/mcp">
|
||||
@@ -70,13 +68,6 @@ const SettingsPage: FC = () => {
|
||||
{t('settings.mcp.title')}
|
||||
</MenuItem>
|
||||
</MenuItemLink>
|
||||
<MenuItemLink to="/settings/knowledge">
|
||||
<MenuItem className={isRoute('/settings/knowledge')}>
|
||||
<FileSearchOutlined />
|
||||
{t('settings.knowledge.title')}
|
||||
</MenuItem>
|
||||
</MenuItemLink>
|
||||
|
||||
<MenuItemLink to="/settings/general">
|
||||
<MenuItem className={isRoute('/settings/general')}>
|
||||
<SettingOutlined />
|
||||
@@ -118,9 +109,8 @@ const SettingsPage: FC = () => {
|
||||
<Routes>
|
||||
<Route path="provider" element={<ProvidersList />} />
|
||||
<Route path="model" element={<ModelSettings />} />
|
||||
<Route path="web-search" element={<WebSearchSettings />} />
|
||||
<Route path="tool/*" element={<ToolSettings />} />
|
||||
<Route path="mcp" element={<MCPSettings />} />
|
||||
<Route path="knowledge" element={<KnowledgeSettings />} />
|
||||
<Route path="general/*" element={<GeneralSettings />} />
|
||||
<Route path="display" element={<DisplaySettings />} />
|
||||
<Route path="data/*" element={<DataSettings />} />
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
import { ExportOutlined } from '@ant-design/icons'
|
||||
import { getOcrProviderLogo, OCR_PROVIDER_CONFIG } from '@renderer/config/ocrProviders'
|
||||
import { useOcrProvider } from '@renderer/hooks/useKnowledge'
|
||||
import { useOcrProvider } from '@renderer/hooks/useOcr'
|
||||
import { formatApiKeys } from '@renderer/services/ApiService'
|
||||
import { OcrProvider } from '@renderer/types'
|
||||
import { hasObjectKey } from '@renderer/utils'
|
||||
@@ -10,14 +10,14 @@ import { FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { SettingHelpLink, SettingHelpText, SettingHelpTextRow, SettingSubtitle, SettingTitle } from '..'
|
||||
import { SettingHelpLink, SettingHelpText, SettingHelpTextRow, SettingSubtitle, SettingTitle } from '../..'
|
||||
|
||||
interface Props {
|
||||
provider: OcrProvider
|
||||
}
|
||||
|
||||
const OcrProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
||||
const { ocrProvider, updateOcrProvider } = useOcrProvider(_provider.id)
|
||||
const { provider: ocrProvider, updateOcrProvider } = useOcrProvider(_provider.id)
|
||||
const { t } = useTranslation()
|
||||
const [apiKey, setApiKey] = useState(ocrProvider.apiKey || '')
|
||||
const [apiHost, setApiHost] = useState(ocrProvider.apiHost || '')
|
||||
@@ -80,7 +80,7 @@ const OcrProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
||||
</Flex>
|
||||
<SettingHelpTextRow style={{ justifyContent: 'space-between', marginTop: 5 }}>
|
||||
<SettingHelpLink target="_blank" href={apiKeyWebsite}>
|
||||
{t('settings.websearch.get_api_key')}
|
||||
{t('settings.provider.get_api_key')}
|
||||
</SettingHelpLink>
|
||||
<SettingHelpText>{t('settings.provider.api_key.tip')}</SettingHelpText>
|
||||
</SettingHelpTextRow>
|
||||
+22
-14
@@ -1,44 +1,52 @@
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import { useOcrProviders } from '@renderer/hooks/useKnowledge'
|
||||
import { useDefaultOcrProvider, useOcrProviders } from '@renderer/hooks/useOcr'
|
||||
import { OcrProvider } from '@renderer/types'
|
||||
import { Select } from 'antd'
|
||||
import { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '../..'
|
||||
import OcrProviderSettings from './OcrProviderSettings'
|
||||
|
||||
const KnowledgeSettings: FC = () => {
|
||||
const { ocrProviders } = useOcrProviders()
|
||||
const { provider: defaultProvider, setDefaultOcrProvider } = useDefaultOcrProvider()
|
||||
const { t } = useTranslation()
|
||||
const [selectedProvider, setSelectedProvider] = useState<OcrProvider>(ocrProviders[0])
|
||||
const [selectedProvider, setSelectedProvider] = useState<OcrProvider | undefined>(defaultProvider)
|
||||
const { theme: themeMode } = useTheme()
|
||||
|
||||
function updateSelectedOcrProvider(providerId: string) {
|
||||
const provider = ocrProviders.find((p) => p.id === providerId)
|
||||
if (!provider) {
|
||||
return
|
||||
}
|
||||
setDefaultOcrProvider(provider)
|
||||
setSelectedProvider(provider)
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingContainer theme={themeMode}>
|
||||
<SettingGroup theme={themeMode}>
|
||||
<SettingTitle>{t('settings.knowledge.ocr.title')}</SettingTitle>
|
||||
<SettingTitle>{t('settings.tool.ocr.title')}</SettingTitle>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.knowledge.ocr.provider')}</SettingRowTitle>
|
||||
<SettingRowTitle>{t('settings.tool.ocr.provider')}</SettingRowTitle>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<Select
|
||||
value={selectedProvider?.id}
|
||||
style={{ width: '200px' }}
|
||||
onChange={(value: string) => {
|
||||
const provider = ocrProviders.find((p) => p.id === value)
|
||||
if (!provider) return
|
||||
setSelectedProvider(provider)
|
||||
}}
|
||||
placeholder={t('settings.websearch.search_provider_placeholder')}
|
||||
onChange={(value: string) => updateSelectedOcrProvider(value)}
|
||||
placeholder={t('settings.tool.ocr.provider_placeholder')}
|
||||
options={ocrProviders.map((p) => ({ value: p.id, label: p.name }))}
|
||||
/>
|
||||
</div>
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
<SettingGroup theme={themeMode}>
|
||||
{selectedProvider && <OcrProviderSettings provider={selectedProvider} />}
|
||||
</SettingGroup>
|
||||
{selectedProvider && (
|
||||
<SettingGroup theme={themeMode}>
|
||||
<OcrProviderSettings provider={selectedProvider} />
|
||||
</SettingGroup>
|
||||
)}
|
||||
</SettingContainer>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { InfoCircleOutlined } from '@ant-design/icons'
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import { setEnhanceMode, setMaxResult, setSearchWithTime } from '@renderer/store/websearch'
|
||||
import { Slider, Switch, Tooltip } from 'antd'
|
||||
import { t } from 'i18next'
|
||||
import { FC } from 'react'
|
||||
|
||||
import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '../..'
|
||||
|
||||
const BasicSettings: FC = () => {
|
||||
const { theme } = useTheme()
|
||||
const searchWithTime = useAppSelector((state) => state.websearch.searchWithTime)
|
||||
const enhanceMode = useAppSelector((state) => state.websearch.enhanceMode)
|
||||
const maxResults = useAppSelector((state) => state.websearch.maxResults)
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
return (
|
||||
<SettingGroup theme={theme} style={{ paddingBottom: 8 }}>
|
||||
<SettingTitle>{t('settings.general.title')}</SettingTitle>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.tool.websearch.search_with_time')}</SettingRowTitle>
|
||||
<Switch checked={searchWithTime} onChange={(checked) => dispatch(setSearchWithTime(checked))} />
|
||||
</SettingRow>
|
||||
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>
|
||||
{t('settings.tool.websearch.enhance_mode')}
|
||||
<Tooltip title={t('settings.tool.websearch.enhance_mode_tooltip')} placement="right">
|
||||
<InfoCircleOutlined style={{ marginLeft: 5, color: 'var(--color-icon)', cursor: 'pointer' }} />
|
||||
</Tooltip>
|
||||
</SettingRowTitle>
|
||||
<Switch checked={enhanceMode} onChange={(checked) => dispatch(setEnhanceMode(checked))} />
|
||||
</SettingRow>
|
||||
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.tool.websearch.search_max_result')}</SettingRowTitle>
|
||||
<Slider
|
||||
defaultValue={maxResults}
|
||||
style={{ width: '200px' }}
|
||||
min={1}
|
||||
max={20}
|
||||
step={1}
|
||||
marks={{ 1: '1', 5: t('settings.tool.websearch.search_result_default'), 20: '20' }}
|
||||
onChangeComplete={(value) => dispatch(setMaxResult(value))}
|
||||
/>
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
)
|
||||
}
|
||||
export default BasicSettings
|
||||
+5
-5
@@ -7,7 +7,7 @@ import TextArea from 'antd/es/input/TextArea'
|
||||
import { t } from 'i18next'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
|
||||
import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||
import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '../..'
|
||||
|
||||
const BlacklistSettings: FC = () => {
|
||||
const [errFormat, setErrFormat] = useState(false)
|
||||
@@ -45,22 +45,22 @@ const BlacklistSettings: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<SettingGroup theme={theme}>
|
||||
<SettingTitle>{t('settings.websearch.blacklist')}</SettingTitle>
|
||||
<SettingTitle>{t('settings.tool.websearch.blacklist')}</SettingTitle>
|
||||
<SettingDivider />
|
||||
<SettingRow style={{ marginBottom: 10 }}>
|
||||
<SettingRowTitle>{t('settings.websearch.blacklist_description')}</SettingRowTitle>
|
||||
<SettingRowTitle>{t('settings.tool.websearch.blacklist_description')}</SettingRowTitle>
|
||||
</SettingRow>
|
||||
<TextArea
|
||||
value={blacklistInput}
|
||||
onChange={(e) => setBlacklistInput(e.target.value)}
|
||||
placeholder={t('settings.websearch.blacklist_tooltip')}
|
||||
placeholder={t('settings.tool.websearch.blacklist_tooltip')}
|
||||
autoSize={{ minRows: 4, maxRows: 8 }}
|
||||
rows={4}
|
||||
/>
|
||||
<Button onClick={() => updateManualBlacklist(blacklistInput)} style={{ marginTop: 10 }}>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
{errFormat && <Alert message={t('settings.websearch.blacklist_tooltip')} type="error" />}
|
||||
{errFormat && <Alert message={t('settings.tool.websearch.blacklist_tooltip')} type="error" />}
|
||||
</SettingGroup>
|
||||
</>
|
||||
)
|
||||
+24
-10
@@ -11,8 +11,8 @@ import { FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { SettingHelpLink, SettingHelpText, SettingHelpTextRow, SettingSubtitle, SettingTitle } from '..'
|
||||
import ApiCheckPopup from '../ProviderSettings/ApiCheckPopup'
|
||||
import { SettingHelpLink, SettingHelpText, SettingHelpTextRow, SettingSubtitle, SettingTitle } from '../..'
|
||||
import ApiCheckPopup from '../../ProviderSettings/ApiCheckPopup'
|
||||
|
||||
interface Props {
|
||||
provider: WebSearchProvider
|
||||
@@ -51,7 +51,7 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
||||
async function checkSearch() {
|
||||
if (!provider) {
|
||||
window.message.error({
|
||||
content: t('settings.websearch.no_provider_selected'),
|
||||
content: t('settings.no_provider_selected'),
|
||||
duration: 3,
|
||||
icon: <InfoCircleOutlined />,
|
||||
key: 'no-provider-selected'
|
||||
@@ -88,7 +88,9 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
||||
key: 'api-check',
|
||||
style: { marginTop: '3vh' },
|
||||
duration: valid ? 2 : 8,
|
||||
content: valid ? t('settings.websearch.check_success') : t('settings.websearch.check_failed') + errorMessage
|
||||
content: valid
|
||||
? t('settings.tool.websearch.check_success')
|
||||
: t('settings.tool.websearch.check_failed') + errorMessage
|
||||
})
|
||||
|
||||
setApiValid(valid)
|
||||
@@ -99,7 +101,7 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
||||
key: 'check-search-error',
|
||||
style: { marginTop: '3vh' },
|
||||
duration: 8,
|
||||
content: t('settings.websearch.check_failed')
|
||||
content: t('settings.tool.websearch.check_failed')
|
||||
})
|
||||
} finally {
|
||||
setApiChecking(false)
|
||||
@@ -145,12 +147,18 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
||||
type={apiValid ? 'primary' : 'default'}
|
||||
onClick={checkSearch}
|
||||
disabled={apiChecking}>
|
||||
{apiChecking ? <LoadingOutlined spin /> : apiValid ? <CheckOutlined /> : t('settings.websearch.check')}
|
||||
{apiChecking ? (
|
||||
<LoadingOutlined spin />
|
||||
) : apiValid ? (
|
||||
<CheckOutlined />
|
||||
) : (
|
||||
t('settings.tool.websearch.check')
|
||||
)}
|
||||
</Button>
|
||||
</Flex>
|
||||
<SettingHelpTextRow style={{ justifyContent: 'space-between', marginTop: 5 }}>
|
||||
<SettingHelpLink target="_blank" href={apiKeyWebsite}>
|
||||
{t('settings.websearch.get_api_key')}
|
||||
{t('settings.tool.websearch.get_api_key')}
|
||||
</SettingHelpLink>
|
||||
<SettingHelpText>{t('settings.provider.api_key.tip')}</SettingHelpText>
|
||||
</SettingHelpTextRow>
|
||||
@@ -160,12 +168,12 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
||||
{hasObjectKey(provider, 'apiHost') && (
|
||||
<>
|
||||
<SettingSubtitle style={{ marginTop: 5, marginBottom: 10 }}>
|
||||
{t('settings.provider.api_host')}
|
||||
{t('settings.tool.provider.api_host')}
|
||||
</SettingSubtitle>
|
||||
<Flex>
|
||||
<Input
|
||||
value={apiHost}
|
||||
placeholder={t('settings.provider.api_host')}
|
||||
placeholder={t('settings.tool.provider.api_host')}
|
||||
onChange={(e) => setApiHost(e.target.value)}
|
||||
onBlur={onUpdateApiHost}
|
||||
/>
|
||||
@@ -174,7 +182,13 @@ const WebSearchProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
||||
type={apiValid ? 'primary' : 'default'}
|
||||
onClick={checkSearch}
|
||||
disabled={apiChecking}>
|
||||
{apiChecking ? <LoadingOutlined spin /> : apiValid ? <CheckOutlined /> : t('settings.websearch.check')}
|
||||
{apiChecking ? (
|
||||
<LoadingOutlined spin />
|
||||
) : apiValid ? (
|
||||
<CheckOutlined />
|
||||
) : (
|
||||
t('settings.tool.websearch.check')
|
||||
)}
|
||||
</Button>
|
||||
</Flex>
|
||||
</>
|
||||
+9
-7
@@ -5,7 +5,7 @@ import { Select } from 'antd'
|
||||
import { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '../..'
|
||||
import BasicSettings from './BasicSettings'
|
||||
import BlacklistSettings from './BlacklistSettings'
|
||||
import WebSearchProviderSetting from './WebSearchProviderSetting'
|
||||
@@ -29,24 +29,26 @@ const WebSearchSettings: FC = () => {
|
||||
return (
|
||||
<SettingContainer theme={themeMode}>
|
||||
<SettingGroup theme={themeMode}>
|
||||
<SettingTitle>{t('settings.websearch.title')}</SettingTitle>
|
||||
<SettingTitle>{t('settings.tool.websearch.title')}</SettingTitle>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.websearch.search_provider')}</SettingRowTitle>
|
||||
<SettingRowTitle>{t('settings.tool.websearch.search_provider')}</SettingRowTitle>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<Select
|
||||
value={selectedProvider?.id}
|
||||
style={{ width: '200px' }}
|
||||
onChange={(value: string) => updateSelectedWebSearchProvider(value)}
|
||||
placeholder={t('settings.websearch.search_provider_placeholder')}
|
||||
placeholder={t('settings.tool.websearch.search_provider_placeholder')}
|
||||
options={providers.map((p) => ({ value: p.id, label: p.name }))}
|
||||
/>
|
||||
</div>
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
<SettingGroup theme={themeMode}>
|
||||
{selectedProvider && <WebSearchProviderSetting provider={selectedProvider} />}
|
||||
</SettingGroup>
|
||||
{selectedProvider && (
|
||||
<SettingGroup theme={themeMode}>
|
||||
<WebSearchProviderSetting provider={selectedProvider} />
|
||||
</SettingGroup>
|
||||
)}
|
||||
<BasicSettings />
|
||||
<BlacklistSettings />
|
||||
</SettingContainer>
|
||||
@@ -0,0 +1,59 @@
|
||||
import { GlobalOutlined } from '@ant-design/icons'
|
||||
import OcrIcon from '@renderer/components/Icons/OcrIcon'
|
||||
import { HStack } from '@renderer/components/Layout'
|
||||
import ListItem from '@renderer/components/ListItem'
|
||||
import { theme } from 'antd'
|
||||
import { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { SettingContainer } from '..'
|
||||
import OcrSettings from './OcrSettings'
|
||||
import WebSearchSettings from './WebSearchSettings'
|
||||
|
||||
const ToolSettings: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const [menu, setMenu] = useState<string>('web-search')
|
||||
const menuItems = [
|
||||
{ key: 'web-search', title: 'settings.tool.websearch.title', icon: <GlobalOutlined style={{ fontSize: 16 }} /> },
|
||||
{ key: 'ocr', title: 'settings.tool.ocr.title', icon: <OcrIcon /> }
|
||||
]
|
||||
return (
|
||||
<Container>
|
||||
<MenuList>
|
||||
{menuItems.map((item) => (
|
||||
<ListItem
|
||||
key={item.key}
|
||||
title={t(item.title)}
|
||||
active={menu === item.key}
|
||||
onClick={() => setMenu(item.key)}
|
||||
titleStyle={{ fontWeight: 500 }}
|
||||
icon={item.icon}
|
||||
/>
|
||||
))}
|
||||
</MenuList>
|
||||
<SettingContainer theme={theme} style={{ display: 'flex', flex: 1 }}>
|
||||
{menu == 'web-search' && <WebSearchSettings />}
|
||||
{menu == 'ocr' && <OcrSettings />}
|
||||
</SettingContainer>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled(HStack)`
|
||||
flex: 1;
|
||||
`
|
||||
const MenuList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
width: var(--settings-width);
|
||||
padding: 12px;
|
||||
border-right: 0.5px solid var(--color-border);
|
||||
height: 100%;
|
||||
.iconfont {
|
||||
color: var(--color-text-2);
|
||||
line-height: 16px;
|
||||
}
|
||||
`
|
||||
export default ToolSettings
|
||||
@@ -1,55 +0,0 @@
|
||||
import { InfoCircleOutlined } from '@ant-design/icons'
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import { setEnhanceMode, setMaxResult, setSearchWithTime } from '@renderer/store/websearch'
|
||||
import { Slider, Switch, Tooltip } from 'antd'
|
||||
import { t } from 'i18next'
|
||||
import { FC } from 'react'
|
||||
|
||||
import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||
|
||||
const BasicSettings: FC = () => {
|
||||
const { theme } = useTheme()
|
||||
const searchWithTime = useAppSelector((state) => state.websearch.searchWithTime)
|
||||
const enhanceMode = useAppSelector((state) => state.websearch.enhanceMode)
|
||||
const maxResults = useAppSelector((state) => state.websearch.maxResults)
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingGroup theme={theme} style={{ paddingBottom: 8 }}>
|
||||
<SettingTitle>{t('settings.general.title')}</SettingTitle>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.websearch.search_with_time')}</SettingRowTitle>
|
||||
<Switch checked={searchWithTime} onChange={(checked) => dispatch(setSearchWithTime(checked))} />
|
||||
</SettingRow>
|
||||
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>
|
||||
{t('settings.websearch.enhance_mode')}
|
||||
<Tooltip title={t('settings.websearch.enhance_mode_tooltip')} placement="right">
|
||||
<InfoCircleOutlined style={{ marginLeft: 5, color: 'var(--color-icon)', cursor: 'pointer' }} />
|
||||
</Tooltip>
|
||||
</SettingRowTitle>
|
||||
<Switch checked={enhanceMode} onChange={(checked) => dispatch(setEnhanceMode(checked))} />
|
||||
</SettingRow>
|
||||
<SettingDivider style={{ marginTop: 15, marginBottom: 12 }} />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.websearch.search_max_result')}</SettingRowTitle>
|
||||
<Slider
|
||||
defaultValue={maxResults}
|
||||
style={{ width: '200px' }}
|
||||
min={1}
|
||||
max={20}
|
||||
step={1}
|
||||
marks={{ 1: '1', 5: t('settings.websearch.search_result_default'), 20: '20' }}
|
||||
onChangeComplete={(value) => dispatch(setMaxResult(value))}
|
||||
/>
|
||||
</SettingRow>
|
||||
</SettingGroup>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default BasicSettings
|
||||
@@ -13,6 +13,7 @@ import mcp from './mcp'
|
||||
import messagesReducer from './messages'
|
||||
import migrate from './migrate'
|
||||
import minapps from './minapps'
|
||||
import ocr from './ocr'
|
||||
import paintings from './paintings'
|
||||
import runtime from './runtime'
|
||||
import settings from './settings'
|
||||
@@ -33,14 +34,15 @@ const rootReducer = combineReducers({
|
||||
websearch,
|
||||
mcp,
|
||||
copilot,
|
||||
messages: messagesReducer
|
||||
messages: messagesReducer,
|
||||
ocr
|
||||
})
|
||||
|
||||
const persistedReducer = persistReducer(
|
||||
{
|
||||
key: 'cherry-studio',
|
||||
storage,
|
||||
version: 83,
|
||||
version: 84,
|
||||
blacklist: ['runtime', 'messages'],
|
||||
migrate
|
||||
},
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||
import FileManager from '@renderer/services/FileManager'
|
||||
import { FileType, KnowledgeBase, KnowledgeItem, OcrProvider, ProcessingStatus } from '@renderer/types'
|
||||
import { FileType, KnowledgeBase, KnowledgeItem, ProcessingStatus } from '@renderer/types'
|
||||
|
||||
export interface KnowledgeState {
|
||||
bases: KnowledgeBase[]
|
||||
ocrProviders: OcrProvider[]
|
||||
}
|
||||
|
||||
const initialState: KnowledgeState = {
|
||||
bases: [],
|
||||
ocrProviders: []
|
||||
bases: []
|
||||
}
|
||||
|
||||
const knowledgeSlice = createSlice({
|
||||
@@ -185,17 +183,6 @@ const knowledgeSlice = createSlice({
|
||||
item.uniqueIds = action.payload.uniqueIds
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateOcrProviders(state, action: PayloadAction<OcrProvider[]>) {
|
||||
state.ocrProviders = action.payload
|
||||
},
|
||||
|
||||
updateOcrProvider(state, action: PayloadAction<OcrProvider>) {
|
||||
const index = state.ocrProviders.findIndex((provider) => provider.id === action.payload.id)
|
||||
if (index !== -1) {
|
||||
state.ocrProviders[index] = action.payload
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -214,9 +201,7 @@ export const {
|
||||
updateItemProcessingStatus,
|
||||
clearCompletedProcessing,
|
||||
clearAllProcessing,
|
||||
updateBaseItemUniqueId,
|
||||
updateOcrProviders,
|
||||
updateOcrProvider
|
||||
updateBaseItemUniqueId
|
||||
} = knowledgeSlice.actions
|
||||
|
||||
export default knowledgeSlice.reducer
|
||||
|
||||
@@ -774,15 +774,6 @@ const migrateConfig = {
|
||||
return state
|
||||
},
|
||||
'82': (state: RootState) => {
|
||||
if (!state.knowledge.ocrProviders) {
|
||||
state.knowledge.ocrProviders = []
|
||||
}
|
||||
state.knowledge.ocrProviders.push({
|
||||
id: 'doc2x',
|
||||
name: 'Doc2x',
|
||||
apiKey: '',
|
||||
apiHost: 'https://v2.doc2x.noedgeai.com'
|
||||
})
|
||||
const runtimeState = state.runtime as any
|
||||
if (runtimeState?.webdavSync) {
|
||||
state.backup = state.backup || {}
|
||||
@@ -804,6 +795,19 @@ const migrateConfig = {
|
||||
state.settings.launchToTray = false
|
||||
state.settings.trayOnClose = true
|
||||
return state
|
||||
},
|
||||
'84': (state: RootState) => {
|
||||
if (!state.ocr.providers) {
|
||||
state.ocr.providers = [
|
||||
{
|
||||
id: 'doc2x',
|
||||
name: 'Doc2x',
|
||||
apiKey: '',
|
||||
apiHost: 'https://v2.doc2x.noedgeai.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||
import { OcrProvider } from '@renderer/types'
|
||||
|
||||
export interface OcrState {
|
||||
providers: OcrProvider[]
|
||||
defaultProvider: string
|
||||
}
|
||||
|
||||
const initialState: OcrState = {
|
||||
providers: [
|
||||
{
|
||||
id: 'doc2x',
|
||||
name: 'Doc2x',
|
||||
apiKey: '',
|
||||
apiHost: 'https://v2.doc2x.noedgeai.com'
|
||||
}
|
||||
],
|
||||
defaultProvider: ''
|
||||
}
|
||||
const ocrSlice = createSlice({
|
||||
name: 'ocr',
|
||||
initialState,
|
||||
reducers: {
|
||||
setDefaultOcrProvider(state, action: PayloadAction<string>) {
|
||||
state.defaultProvider = action.payload
|
||||
},
|
||||
setOcrProviders(state, action: PayloadAction<OcrProvider[]>) {
|
||||
state.providers = action.payload
|
||||
},
|
||||
updateOcrProviders(state, action: PayloadAction<OcrProvider[]>) {
|
||||
state.providers = action.payload
|
||||
},
|
||||
updateOcrProvider(state, action: PayloadAction<OcrProvider>) {
|
||||
const index = state.providers.findIndex((provider) => provider.id === action.payload.id)
|
||||
if (index !== -1) {
|
||||
state.providers[index] = action.payload
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const { updateOcrProviders, updateOcrProvider, setDefaultOcrProvider, setOcrProviders } = ocrSlice.actions
|
||||
|
||||
export default ocrSlice.reducer
|
||||
Reference in New Issue
Block a user