mirror of
https://github.com/NovaOSS/nova-betterchat.git
synced 2024-11-25 20:34:00 +01:00
feat: auto generate title setting
This commit is contained in:
parent
4c80898078
commit
4313446dd3
|
@ -22,5 +22,6 @@
|
||||||
"lightMode": "Light Mode",
|
"lightMode": "Light Mode",
|
||||||
"darkMode": "Dark Mode",
|
"darkMode": "Dark Mode",
|
||||||
"setting": "Settings",
|
"setting": "Settings",
|
||||||
"image": "Image"
|
"image": "Image",
|
||||||
|
"autoTitle": "Auto generate title"
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,6 @@
|
||||||
"lightMode": "亮色模式",
|
"lightMode": "亮色模式",
|
||||||
"darkMode": "黑暗模式",
|
"darkMode": "黑暗模式",
|
||||||
"setting": "设置",
|
"setting": "设置",
|
||||||
"image": "图片"
|
"image": "图片",
|
||||||
|
"autoTitle": "自动生成标题"
|
||||||
}
|
}
|
||||||
|
|
28
src/components/SettingsMenu/AutoTitleToggle.tsx
Normal file
28
src/components/SettingsMenu/AutoTitleToggle.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import useStore from '@store/store';
|
||||||
|
import Toggle from '@components/Toggle';
|
||||||
|
|
||||||
|
const AutoTitleToggle = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const setAutoTitle = useStore((state) => state.setAutoTitle);
|
||||||
|
|
||||||
|
const [isChecked, setIsChecked] = useState<boolean>(
|
||||||
|
useStore.getState().autoTitle
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAutoTitle(isChecked);
|
||||||
|
}, [isChecked]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Toggle
|
||||||
|
label={t('autoTitle') as string}
|
||||||
|
isChecked={isChecked}
|
||||||
|
setIsChecked={setIsChecked}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AutoTitleToggle;
|
|
@ -6,6 +6,7 @@ import PopupModal from '@components/PopupModal';
|
||||||
import SettingIcon from '@icon/SettingIcon';
|
import SettingIcon from '@icon/SettingIcon';
|
||||||
import ThemeSwitcher from '@components/Menu/MenuOptions/ThemeSwitcher';
|
import ThemeSwitcher from '@components/Menu/MenuOptions/ThemeSwitcher';
|
||||||
import LanguageSelector from '@components/LanguageSelector';
|
import LanguageSelector from '@components/LanguageSelector';
|
||||||
|
import AutoTitleToggle from './AutoTitleToggle';
|
||||||
|
|
||||||
const SettingsMenu = () => {
|
const SettingsMenu = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
@ -35,6 +36,7 @@ const SettingsMenu = () => {
|
||||||
<div className='p-6 border-b border-gray-200 dark:border-gray-600 flex flex-col items-center gap-4'>
|
<div className='p-6 border-b border-gray-200 dark:border-gray-600 flex flex-col items-center gap-4'>
|
||||||
<LanguageSelector />
|
<LanguageSelector />
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher />
|
||||||
|
<AutoTitleToggle />
|
||||||
</div>
|
</div>
|
||||||
</PopupModal>
|
</PopupModal>
|
||||||
)}
|
)}
|
||||||
|
|
30
src/components/Toggle/Toggle.tsx
Normal file
30
src/components/Toggle/Toggle.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Toggle = ({
|
||||||
|
label,
|
||||||
|
isChecked,
|
||||||
|
setIsChecked,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
isChecked: boolean;
|
||||||
|
setIsChecked: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<label className='relative flex items-center cursor-pointer'>
|
||||||
|
<input
|
||||||
|
type='checkbox'
|
||||||
|
className='sr-only peer'
|
||||||
|
checked={isChecked}
|
||||||
|
onChange={() => {
|
||||||
|
setIsChecked((prev) => !prev);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="w-9 h-5 bg-gray-200 dark:bg-gray-600 rounded-full peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-green-500/70"></div>
|
||||||
|
<span className='ml-3 text-sm font-medium text-gray-900 dark:text-gray-300'>
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Toggle;
|
1
src/components/Toggle/index.ts
Normal file
1
src/components/Toggle/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { default } from './Toggle';
|
|
@ -119,7 +119,11 @@ const useSubmit = () => {
|
||||||
|
|
||||||
// generate title for new chats
|
// generate title for new chats
|
||||||
const currChats = useStore.getState().chats;
|
const currChats = useStore.getState().chats;
|
||||||
if (currChats && !currChats[currentChatIndex]?.titleSet) {
|
if (
|
||||||
|
useStore.getState().autoTitle &&
|
||||||
|
currChats &&
|
||||||
|
!currChats[currentChatIndex]?.titleSet
|
||||||
|
) {
|
||||||
const messages_length = currChats[currentChatIndex].messages.length;
|
const messages_length = currChats[currentChatIndex].messages.length;
|
||||||
const assistant_message =
|
const assistant_message =
|
||||||
currChats[currentChatIndex].messages[messages_length - 1].content;
|
currChats[currentChatIndex].messages[messages_length - 1].content;
|
||||||
|
|
|
@ -4,13 +4,16 @@ import { Theme } from '@type/theme';
|
||||||
export interface ConfigSlice {
|
export interface ConfigSlice {
|
||||||
openConfig: boolean;
|
openConfig: boolean;
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
|
autoTitle: boolean;
|
||||||
setOpenConfig: (openConfig: boolean) => void;
|
setOpenConfig: (openConfig: boolean) => void;
|
||||||
setTheme: (theme: Theme) => void;
|
setTheme: (theme: Theme) => void;
|
||||||
|
setAutoTitle: (autoTitle: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
||||||
openConfig: false,
|
openConfig: false,
|
||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
|
autoTitle: false,
|
||||||
setOpenConfig: (openConfig: boolean) => {
|
setOpenConfig: (openConfig: boolean) => {
|
||||||
set((prev: ConfigSlice) => ({
|
set((prev: ConfigSlice) => ({
|
||||||
...prev,
|
...prev,
|
||||||
|
@ -23,4 +26,10 @@ export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
||||||
theme: theme,
|
theme: theme,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
setAutoTitle: (autoTitle: boolean) => {
|
||||||
|
set((prev: ConfigSlice) => ({
|
||||||
|
...prev,
|
||||||
|
autoTitle: autoTitle,
|
||||||
|
}));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import {
|
import {
|
||||||
LocalStorageInterfaceV0ToV1,
|
LocalStorageInterfaceV0ToV1,
|
||||||
LocalStorageInterfaceV1ToV2,
|
LocalStorageInterfaceV1ToV2,
|
||||||
|
LocalStorageInterfaceV2ToV3,
|
||||||
} from '@type/chat';
|
} from '@type/chat';
|
||||||
import { defaultChatConfig } from '@constants/chat';
|
import { defaultChatConfig } from '@constants/chat';
|
||||||
import { defaultAPIEndpoint, officialAPIEndpoint } from '@constants/auth';
|
import { officialAPIEndpoint } from '@constants/auth';
|
||||||
|
|
||||||
export const migrateV0 = (persistedState: LocalStorageInterfaceV0ToV1) => {
|
export const migrateV0 = (persistedState: LocalStorageInterfaceV0ToV1) => {
|
||||||
persistedState.chats.forEach((chat) => {
|
persistedState.chats.forEach((chat) => {
|
||||||
|
@ -20,9 +21,13 @@ export const migrateV1 = (persistedState: LocalStorageInterfaceV1ToV2) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const migrateV2 = (persistedState: LocalStorageInterfaceV1ToV2) => {
|
export const migrateV2 = (persistedState: LocalStorageInterfaceV2ToV3) => {
|
||||||
persistedState.chats.forEach((chat) => {
|
persistedState.chats.forEach((chat) => {
|
||||||
chat.config.top_p = defaultChatConfig.top_p;
|
chat.config = {
|
||||||
chat.config.frequency_penalty = defaultChatConfig.frequency_penalty;
|
...chat.config,
|
||||||
|
top_p: defaultChatConfig.top_p,
|
||||||
|
frequency_penalty: defaultChatConfig.frequency_penalty,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
persistedState.autoTitle = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,6 +35,7 @@ const useStore = create<StoreState>()(
|
||||||
apiFree: state.apiFree,
|
apiFree: state.apiFree,
|
||||||
apiEndpoint: state.apiEndpoint,
|
apiEndpoint: state.apiEndpoint,
|
||||||
theme: state.theme,
|
theme: state.theme,
|
||||||
|
autoTitle: state.autoTitle,
|
||||||
}),
|
}),
|
||||||
version: 3,
|
version: 3,
|
||||||
migrate: (persistedState, version) => {
|
migrate: (persistedState, version) => {
|
||||||
|
|
|
@ -49,4 +49,5 @@ export interface LocalStorageInterfaceV2ToV3 {
|
||||||
apiFreeEndpoint: string;
|
apiFreeEndpoint: string;
|
||||||
apiEndpoint?: string;
|
apiEndpoint?: string;
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
}
|
autoTitle: boolean;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue