import React, {PropsWithChildren, useCallback, useMemo, useState} from 'react' import {setEnvData, setPage} from '../redux/envReducer' import {useAppDispatch, useAppSelector} from '../hooks/redux' import { ASK_ENABLED_DEFAULT, CUSTOM_MODEL_TOKENS, DEFAULT_SERVER_URL_OPENAI, GEMINI_TOKENS, HEADER_HEIGHT, LANGUAGE_DEFAULT, LANGUAGES, MODEL_DEFAULT, MODEL_MAP, MODEL_TIP, MODELS, PAGE_MAIN, PROMPT_DEFAULTS, PROMPT_TYPES, SUMMARIZE_LANGUAGE_DEFAULT, TRANSLATE_FETCH_DEFAULT, TRANSLATE_FETCH_MAX, TRANSLATE_FETCH_MIN, TRANSLATE_FETCH_STEP, WORDS_RATE, } from '../const' import {IoWarning} from 'react-icons/all' import classNames from 'classnames' import toast from 'react-hot-toast' import {useBoolean, useEventTarget} from 'ahooks' import {useEventChecked} from '@kky002/kky-hooks' const Section = (props: { title: ShowElement htmlFor?: string } & PropsWithChildren) => { const {title, htmlFor, children} = props return
{children}
} const FormItem = (props: { title: ShowElement tip?: string htmlFor?: string } & PropsWithChildren) => { const {title, tip, htmlFor, children} = props return
{children}
} const Settings = () => { const dispatch = useAppDispatch() const envData = useAppSelector(state => state.env.envData) const {value: autoExpandValue, onChange: setAutoExpandValue} = useEventChecked(envData.autoExpand) // const {value: autoScrollValue, onChange: setAutoScrollValue} = useEventChecked(envData.autoScroll) const {value: translateEnableValue, onChange: setTranslateEnableValue} = useEventChecked(envData.translateEnable) const {value: summarizeEnableValue, onChange: setSummarizeEnableValue} = useEventChecked(envData.summarizeEnable) const {value: searchEnabledValue, onChange: setSearchEnabledValue} = useEventChecked(envData.searchEnabled) const {value: askEnabledValue, onChange: setAskEnabledValue} = useEventChecked(envData.askEnabled??ASK_ENABLED_DEFAULT) const {value: cnSearchEnabledValue, onChange: setCnSearchEnabledValue} = useEventChecked(envData.cnSearchEnabled) const {value: summarizeFloatValue, onChange: setSummarizeFloatValue} = useEventChecked(envData.summarizeFloat) const [apiKeyValue, { onChange: onChangeApiKeyValue }] = useEventTarget({initialValue: envData.apiKey??''}) const [serverUrlValue, setServerUrlValue] = useState(envData.serverUrl) const [geminiApiKeyValue, { onChange: onChangeGeminiApiKeyValue }] = useEventTarget({initialValue: envData.geminiApiKey??''}) const [languageValue, { onChange: onChangeLanguageValue }] = useEventTarget({initialValue: envData.language??LANGUAGE_DEFAULT}) const [modelValue, { onChange: onChangeModelValue }] = useEventTarget({initialValue: envData.model??MODEL_DEFAULT}) const [customModelValue, { onChange: onChangeCustomModelValue }] = useEventTarget({initialValue: envData.customModel}) const [customModelTokensValue, setCustomModelTokensValue] = useState(envData.customModelTokens) const [summarizeLanguageValue, { onChange: onChangeSummarizeLanguageValue }] = useEventTarget({initialValue: envData.summarizeLanguage??SUMMARIZE_LANGUAGE_DEFAULT}) const [hideOnDisableAutoTranslateValue, setHideOnDisableAutoTranslateValue] = useState(envData.hideOnDisableAutoTranslate) const [themeValue, setThemeValue] = useState(envData.theme) const [fontSizeValue, setFontSizeValue] = useState(envData.fontSize) const [aiTypeValue, setAiTypeValue] = useState(envData.aiType) const [transDisplayValue, setTransDisplayValue] = useState(envData.transDisplay) const [wordsValue, setWordsValue] = useState(envData.words) const [fetchAmountValue, setFetchAmountValue] = useState(envData.fetchAmount??TRANSLATE_FETCH_DEFAULT) const [moreFold, {toggle: toggleMoreFold}] = useBoolean(true) const [promptsFold, {toggle: togglePromptsFold}] = useBoolean(true) const fold = useAppSelector(state => state.env.fold) const totalHeight = useAppSelector(state => state.env.totalHeight) const [promptsValue, setPromptsValue] = useState<{[key: string]: string}>(envData.prompts??{}) // const wordsList = useMemo(() => { // const list = [] // for (let i = WORDS_MIN; i <= WORDS_MAX; i += WORDS_STEP) { // list.push(i) // } // return list // }, []) const transFetchAmountList = useMemo(() => { const list = [] for (let i = TRANSLATE_FETCH_MIN; i <= TRANSLATE_FETCH_MAX; i += TRANSLATE_FETCH_STEP) { list.push(i) } return list }, []) const apiKeySetted = useMemo(() => { if (aiTypeValue === 'gemini') { return !!geminiApiKeyValue } return !!apiKeyValue }, [aiTypeValue, apiKeyValue, geminiApiKeyValue]) const onChangeHideOnDisableAutoTranslate = useCallback((e: any) => { setHideOnDisableAutoTranslateValue(e.target.checked) }, []) const onSave = useCallback(() => { dispatch(setEnvData({ autoExpand: autoExpandValue, aiType: aiTypeValue, apiKey: apiKeyValue, serverUrl: serverUrlValue, model: modelValue, customModel: customModelValue, customModelTokens: customModelTokensValue, geminiApiKey: geminiApiKeyValue, translateEnable: translateEnableValue, language: languageValue, hideOnDisableAutoTranslate: hideOnDisableAutoTranslateValue, theme: themeValue, transDisplay: transDisplayValue, summarizeEnable: summarizeEnableValue, summarizeFloat: summarizeFloatValue, summarizeLanguage: summarizeLanguageValue, words: wordsValue, fetchAmount: fetchAmountValue, fontSize: fontSizeValue, prompts: promptsValue, searchEnabled: searchEnabledValue, cnSearchEnabled: cnSearchEnabledValue, askEnabled: askEnabledValue, })) dispatch(setPage(PAGE_MAIN)) toast.success('保存成功') }, [dispatch, autoExpandValue, aiTypeValue, apiKeyValue, serverUrlValue, modelValue, customModelValue, customModelTokensValue, geminiApiKeyValue, translateEnableValue, languageValue, hideOnDisableAutoTranslateValue, themeValue, transDisplayValue, summarizeEnableValue, summarizeFloatValue, summarizeLanguageValue, wordsValue, fetchAmountValue, fontSizeValue, promptsValue, searchEnabledValue, cnSearchEnabledValue, askEnabledValue]) const onCancel = useCallback(() => { dispatch(setPage(PAGE_MAIN)) }, [dispatch]) const onFetchAmountChange = useCallback((e: any) => { setFetchAmountValue(parseInt(e.target.value)) }, []) const onWordsChange = useCallback((e: any) => { setWordsValue(parseInt(e.target.value)) }, []) const onSel1 = useCallback(() => { setTransDisplayValue('originPrimary') }, []) const onSel2 = useCallback(() => { setTransDisplayValue('targetPrimary') }, []) const onSel3 = useCallback(() => { setTransDisplayValue('target') }, []) const onSelTheme1 = useCallback(() => { setThemeValue('system') }, []) const onSelTheme2 = useCallback(() => { setThemeValue('light') }, []) const onSelTheme3 = useCallback(() => { setThemeValue('dark') }, []) const onSelFontSize1 = useCallback(() => { setFontSizeValue('normal') }, []) const onSelFontSize2 = useCallback(() => { setFontSizeValue('large') }, []) const onSelOpenai = useCallback(() => { setAiTypeValue('openai') }, []) const onSelGemini = useCallback(() => { setAiTypeValue('gemini') }, []) return
{(!aiTypeValue || aiTypeValue === 'openai') &&
setServerUrlValue(e.target.value)}/>
【官方地址】
官方网址:点击访问
{/*
【第三方代理】
*/} {/*
代理网址:点击访问
*/} {/* */} {/*
目前价格不到官方价格的6折
*/}
{MODEL_TIP}
{modelValue === 'custom' && } {modelValue === 'custom' && setCustomModelTokensValue(e.target.value ? parseInt(e.target.value) : undefined)}/> }
} {aiTypeValue === 'gemini' &&
官方网址:Google AI Studio (目前免费)
谷歌模型安全要求比较高,有些视频可能无法生成总结!
}
翻译配置 {!apiKeySetted &&
}
}>
{transFetchAmountList.map(amount => {amount})}
总结配置 {!apiKeySetted &&
}
}>
setWordsValue(e.target.value?parseInt(e.target.value):undefined)}/> {/* */} {/*
*/} {/* {wordsList.map(words => {words})} */} {/*
*/}
当前选择的模型的分段字数上限是{aiTypeValue === 'gemini'?GEMINI_TOKENS:(MODEL_MAP[modelValue??MODEL_DEFAULT]?.tokens??'未知')} (太接近上限总结会报错)
搜索配置 }>
提问配置 }>
点击{promptsFold ? '展开' : '折叠'}
{!promptsFold && PROMPT_TYPES.map((item, idx) =>
{item.name}
{ setPromptsValue({ ...promptsValue, // @ts-expect-error [item.type]: PROMPT_DEFAULTS[item.type] ?? '' }) }}>点击填充默认
} htmlFor={`prompt-${item.type}`}>