mirror of
https://github.com/NovaOSS/nova-betterchat.git
synced 2024-11-29 10:53:59 +01:00
Add simple mode (#113)
* Add simple mode The current UI is too complicated for some first time users. This commit adds a "Advanced mode" toggle that defaults on but can be turned off. When the toggle if switched off, the simple mode is activated and following UI elements are hided: - Model parameters setting - Role switch - Save button (This means user must save *and* submit in simple mode.) * Simple mode: Fix ctrl-enter in non-sticky blocks * i18n * hide token count * add back save button * remove unused variables --------- Co-authored-by: Jing Hua <tohjinghua123@gmail.com>
This commit is contained in:
parent
f5f777b54b
commit
c2d833b332
|
@ -24,6 +24,7 @@
|
||||||
"setting": "Indstillinger",
|
"setting": "Indstillinger",
|
||||||
"image": "Billede",
|
"image": "Billede",
|
||||||
"autoTitle": "Auto generer titel",
|
"autoTitle": "Auto generer titel",
|
||||||
|
"advancedMode": "Avanceret tilstand",
|
||||||
"prompt": "Opgave",
|
"prompt": "Opgave",
|
||||||
"promptLibrary": "Opgavebibliotek",
|
"promptLibrary": "Opgavebibliotek",
|
||||||
"name": "Navn",
|
"name": "Navn",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "Settings",
|
"setting": "Settings",
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"autoTitle": "Auto generate title",
|
"autoTitle": "Auto generate title",
|
||||||
|
"advancedMode": "Advanced mode",
|
||||||
"prompt": "Prompt",
|
"prompt": "Prompt",
|
||||||
"promptLibrary": "Prompt Library",
|
"promptLibrary": "Prompt Library",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "Settings",
|
"setting": "Settings",
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"autoTitle": "Auto generate title",
|
"autoTitle": "Auto generate title",
|
||||||
|
"advancedMode": "Advanced mode",
|
||||||
"prompt": "Prompt",
|
"prompt": "Prompt",
|
||||||
"promptLibrary": "Prompt Library",
|
"promptLibrary": "Prompt Library",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "Ajustes",
|
"setting": "Ajustes",
|
||||||
"image": "Imagen",
|
"image": "Imagen",
|
||||||
"autoTitle": "Generar automáticamente el título de la conversación.",
|
"autoTitle": "Generar automáticamente el título de la conversación.",
|
||||||
|
"advancedMode": "Modo avanzado",
|
||||||
"prompt": "Prompt",
|
"prompt": "Prompt",
|
||||||
"promptLibrary": "Librería de Prompts",
|
"promptLibrary": "Librería de Prompts",
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "Paramètres",
|
"setting": "Paramètres",
|
||||||
"image": "Image",
|
"image": "Image",
|
||||||
"autoTitle": "Générer le titre automatiquement",
|
"autoTitle": "Générer le titre automatiquement",
|
||||||
|
"advancedMode": "Mode avancé",
|
||||||
"prompt": "Incitation",
|
"prompt": "Incitation",
|
||||||
"promptLibrary": "Bibliothèque de prompt",
|
"promptLibrary": "Bibliothèque de prompt",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "Impostazioni",
|
"setting": "Impostazioni",
|
||||||
"image": "Immagine",
|
"image": "Immagine",
|
||||||
"autoTitle": "Genera automaticamente il titolo",
|
"autoTitle": "Genera automaticamente il titolo",
|
||||||
|
"advancedMode": "Modalità avanzata",
|
||||||
"prompt": "Prompt",
|
"prompt": "Prompt",
|
||||||
"promptLibrary": "Libreria Prompt",
|
"promptLibrary": "Libreria Prompt",
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "設定",
|
"setting": "設定",
|
||||||
"image": "画像",
|
"image": "画像",
|
||||||
"autoTitle": "タイトルを自動生成",
|
"autoTitle": "タイトルを自動生成",
|
||||||
|
"advancedMode": "上級モード",
|
||||||
"prompt": "プロンプト",
|
"prompt": "プロンプト",
|
||||||
"promptLibrary": "プロンプトライブラリ",
|
"promptLibrary": "プロンプトライブラリ",
|
||||||
"name": "名前",
|
"name": "名前",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "Tetapan",
|
"setting": "Tetapan",
|
||||||
"image": "Imej",
|
"image": "Imej",
|
||||||
"autoTitle": "Jana tajuk secara automatik",
|
"autoTitle": "Jana tajuk secara automatik",
|
||||||
|
"advancedMode": "Mod lanjutan",
|
||||||
"prompt": "Arahan",
|
"prompt": "Arahan",
|
||||||
"promptLibrary": "Pustaka Arahan",
|
"promptLibrary": "Pustaka Arahan",
|
||||||
"name": "Nama",
|
"name": "Nama",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "Innstillinger",
|
"setting": "Innstillinger",
|
||||||
"image": "Bilde",
|
"image": "Bilde",
|
||||||
"autoTitle": "Auto generer tittel",
|
"autoTitle": "Auto generer tittel",
|
||||||
|
"advancedMode": "Avansert modus",
|
||||||
"prompt": "Oppgave",
|
"prompt": "Oppgave",
|
||||||
"promptLibrary": "Oppgavebibliotek",
|
"promptLibrary": "Oppgavebibliotek",
|
||||||
"name": "Navn",
|
"name": "Navn",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "Inställningar",
|
"setting": "Inställningar",
|
||||||
"image": "Bild",
|
"image": "Bild",
|
||||||
"autoTitle": "Auto generera titel",
|
"autoTitle": "Auto generera titel",
|
||||||
|
"advancedMode": "Avancerat läge",
|
||||||
"prompt": "Uppmaning",
|
"prompt": "Uppmaning",
|
||||||
"promptLibrary": "Uppmaningsbibliotek",
|
"promptLibrary": "Uppmaningsbibliotek",
|
||||||
"name": "Namn",
|
"name": "Namn",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "设置",
|
"setting": "设置",
|
||||||
"image": "图片",
|
"image": "图片",
|
||||||
"autoTitle": "自动生成标题",
|
"autoTitle": "自动生成标题",
|
||||||
|
"advancedMode": "高级模式",
|
||||||
"prompt": "提示词",
|
"prompt": "提示词",
|
||||||
"promptLibrary": "提示词资料库",
|
"promptLibrary": "提示词资料库",
|
||||||
"name": "名称",
|
"name": "名称",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "設定",
|
"setting": "設定",
|
||||||
"image": "圖片",
|
"image": "圖片",
|
||||||
"autoTitle": "自動生成標題",
|
"autoTitle": "自動生成標題",
|
||||||
|
"advancedMode": "高級模式",
|
||||||
"prompt": "Prompt",
|
"prompt": "Prompt",
|
||||||
"promptLibrary": "Prompt 資料庫",
|
"promptLibrary": "Prompt 資料庫",
|
||||||
"name": "名",
|
"name": "名",
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"setting": "設定",
|
"setting": "設定",
|
||||||
"image": "圖片",
|
"image": "圖片",
|
||||||
"autoTitle": "自動生成標題",
|
"autoTitle": "自動生成標題",
|
||||||
|
"advancedMode": "高級模式",
|
||||||
"prompt": "提示詞",
|
"prompt": "提示詞",
|
||||||
"promptLibrary": "提示詞資料庫",
|
"promptLibrary": "提示詞資料庫",
|
||||||
"name": "名稱",
|
"name": "名稱",
|
||||||
|
|
|
@ -32,6 +32,7 @@ const ChatContent = () => {
|
||||||
? state.chats[state.currentChatIndex].messages.length
|
? state.chats[state.currentChatIndex].messages.length
|
||||||
: 0
|
: 0
|
||||||
);
|
);
|
||||||
|
const advancedMode = useStore((state) => state.advancedMode);
|
||||||
const generating = useStore.getState().generating;
|
const generating = useStore.getState().generating;
|
||||||
const hideSideMenu = useStore((state) => state.hideSideMenu);
|
const hideSideMenu = useStore((state) => state.hideSideMenu);
|
||||||
|
|
||||||
|
@ -58,8 +59,8 @@ const ChatContent = () => {
|
||||||
className='flex flex-col items-center text-sm dark:bg-gray-800 w-full'
|
className='flex flex-col items-center text-sm dark:bg-gray-800 w-full'
|
||||||
ref={saveRef}
|
ref={saveRef}
|
||||||
>
|
>
|
||||||
<ChatTitle />
|
{advancedMode && <ChatTitle />}
|
||||||
{!generating && messages?.length === 0 && (
|
{!generating && advancedMode && messages?.length === 0 && (
|
||||||
<NewMessageButton messageIndex={-1} />
|
<NewMessageButton messageIndex={-1} />
|
||||||
)}
|
)}
|
||||||
{messages?.map((message, index) => (
|
{messages?.map((message, index) => (
|
||||||
|
@ -69,7 +70,7 @@ const ChatContent = () => {
|
||||||
content={message.content}
|
content={message.content}
|
||||||
messageIndex={index}
|
messageIndex={index}
|
||||||
/>
|
/>
|
||||||
{!generating && <NewMessageButton messageIndex={index} />}
|
{!generating && advancedMode && <NewMessageButton messageIndex={index} />}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,6 +27,7 @@ const Message = React.memo(
|
||||||
sticky?: boolean;
|
sticky?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const hideSideMenu = useStore((state) => state.hideSideMenu);
|
const hideSideMenu = useStore((state) => state.hideSideMenu);
|
||||||
|
const advancedMode = useStore((state) => state.advancedMode);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -43,11 +44,12 @@ const Message = React.memo(
|
||||||
>
|
>
|
||||||
<Avatar role={role} />
|
<Avatar role={role} />
|
||||||
<div className='w-[calc(100%-50px)] '>
|
<div className='w-[calc(100%-50px)] '>
|
||||||
|
{advancedMode &&
|
||||||
<RoleSelector
|
<RoleSelector
|
||||||
role={role}
|
role={role}
|
||||||
messageIndex={messageIndex}
|
messageIndex={messageIndex}
|
||||||
sticky={sticky}
|
sticky={sticky}
|
||||||
/>
|
/>}
|
||||||
<MessageContent
|
<MessageContent
|
||||||
role={role}
|
role={role}
|
||||||
content={content}
|
content={content}
|
||||||
|
|
|
@ -43,10 +43,11 @@ const MessageContent = ({
|
||||||
sticky?: boolean;
|
sticky?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const [isEdit, setIsEdit] = useState<boolean>(sticky);
|
const [isEdit, setIsEdit] = useState<boolean>(sticky);
|
||||||
|
const advancedMode = useStore((state) => state.advancedMode);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative flex flex-col gap-1 md:gap-3 lg:w-[calc(100%-115px)]'>
|
<div className='relative flex flex-col gap-1 md:gap-3 lg:w-[calc(100%-115px)]'>
|
||||||
<div className='flex flex-grow flex-col gap-3'></div>
|
{advancedMode && <div className='flex flex-grow flex-col gap-3'></div>}
|
||||||
{isEdit ? (
|
{isEdit ? (
|
||||||
<EditView
|
<EditView
|
||||||
content={content}
|
content={content}
|
||||||
|
@ -483,6 +484,7 @@ const EditViewButtons = React.memo(
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const generating = useStore.getState().generating;
|
const generating = useStore.getState().generating;
|
||||||
|
const advancedMode = useStore((state) => state.advancedMode);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
|
@ -539,7 +541,7 @@ const EditViewButtons = React.memo(
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{sticky && <TokenCount />}
|
{sticky && advancedMode && <TokenCount />}
|
||||||
<CommandPrompt _setContent={_setContent} />
|
<CommandPrompt _setContent={_setContent} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
28
src/components/SettingsMenu/AdvencedModeToggle.tsx
Normal file
28
src/components/SettingsMenu/AdvencedModeToggle.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 AdvancedModeToggle = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const setAdvancedMode = useStore((state) => state.setAdvancedMode);
|
||||||
|
|
||||||
|
const [isChecked, setIsChecked] = useState<boolean>(
|
||||||
|
useStore.getState().advancedMode
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setAdvancedMode(isChecked);
|
||||||
|
}, [isChecked]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Toggle
|
||||||
|
label={t('advancedMode') as string}
|
||||||
|
isChecked={isChecked}
|
||||||
|
setIsChecked={setIsChecked}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AdvancedModeToggle;
|
|
@ -8,6 +8,7 @@ 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';
|
import AutoTitleToggle from './AutoTitleToggle';
|
||||||
|
import AdvancedModeToggle from './AdvencedModeToggle';
|
||||||
import PromptLibraryMenu from '@components/PromptLibraryMenu';
|
import PromptLibraryMenu from '@components/PromptLibraryMenu';
|
||||||
import ChatConfigMenu from '@components/ChatConfigMenu';
|
import ChatConfigMenu from '@components/ChatConfigMenu';
|
||||||
import EnterToSubmitToggle from './EnterToSubmitToggle';
|
import EnterToSubmitToggle from './EnterToSubmitToggle';
|
||||||
|
@ -44,6 +45,7 @@ const SettingsMenu = () => {
|
||||||
<div className='flex flex-col gap-3'>
|
<div className='flex flex-col gap-3'>
|
||||||
<AutoTitleToggle />
|
<AutoTitleToggle />
|
||||||
<EnterToSubmitToggle />
|
<EnterToSubmitToggle />
|
||||||
|
<AdvancedModeToggle />
|
||||||
</div>
|
</div>
|
||||||
<PromptLibraryMenu />
|
<PromptLibraryMenu />
|
||||||
<ChatConfigMenu />
|
<ChatConfigMenu />
|
||||||
|
|
|
@ -8,6 +8,7 @@ export interface ConfigSlice {
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
autoTitle: boolean;
|
autoTitle: boolean;
|
||||||
hideMenuOptions: boolean;
|
hideMenuOptions: boolean;
|
||||||
|
advancedMode: boolean;
|
||||||
defaultChatConfig: ConfigInterface;
|
defaultChatConfig: ConfigInterface;
|
||||||
defaultSystemMessage: string;
|
defaultSystemMessage: string;
|
||||||
hideSideMenu: boolean;
|
hideSideMenu: boolean;
|
||||||
|
@ -15,6 +16,7 @@ export interface ConfigSlice {
|
||||||
setOpenConfig: (openConfig: boolean) => void;
|
setOpenConfig: (openConfig: boolean) => void;
|
||||||
setTheme: (theme: Theme) => void;
|
setTheme: (theme: Theme) => void;
|
||||||
setAutoTitle: (autoTitle: boolean) => void;
|
setAutoTitle: (autoTitle: boolean) => void;
|
||||||
|
setAdvancedMode: (advancedMode: boolean) => void;
|
||||||
setDefaultChatConfig: (defaultChatConfig: ConfigInterface) => void;
|
setDefaultChatConfig: (defaultChatConfig: ConfigInterface) => void;
|
||||||
setDefaultSystemMessage: (defaultSystemMessage: string) => void;
|
setDefaultSystemMessage: (defaultSystemMessage: string) => void;
|
||||||
setHideMenuOptions: (hideMenuOptions: boolean) => void;
|
setHideMenuOptions: (hideMenuOptions: boolean) => void;
|
||||||
|
@ -29,6 +31,7 @@ export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
||||||
hideSideMenu: false,
|
hideSideMenu: false,
|
||||||
autoTitle: false,
|
autoTitle: false,
|
||||||
enterToSubmit: true,
|
enterToSubmit: true,
|
||||||
|
advancedMode: true,
|
||||||
defaultChatConfig: _defaultChatConfig,
|
defaultChatConfig: _defaultChatConfig,
|
||||||
defaultSystemMessage: _defaultSystemMessage,
|
defaultSystemMessage: _defaultSystemMessage,
|
||||||
setOpenConfig: (openConfig: boolean) => {
|
setOpenConfig: (openConfig: boolean) => {
|
||||||
|
@ -49,6 +52,12 @@ export const createConfigSlice: StoreSlice<ConfigSlice> = (set, get) => ({
|
||||||
autoTitle: autoTitle,
|
autoTitle: autoTitle,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
setAdvancedMode: (advancedMode: boolean) => {
|
||||||
|
set((prev: ConfigSlice) => ({
|
||||||
|
...prev,
|
||||||
|
advancedMode: advancedMode,
|
||||||
|
}));
|
||||||
|
},
|
||||||
setDefaultChatConfig: (defaultChatConfig: ConfigInterface) => {
|
setDefaultChatConfig: (defaultChatConfig: ConfigInterface) => {
|
||||||
set((prev: ConfigSlice) => ({
|
set((prev: ConfigSlice) => ({
|
||||||
...prev,
|
...prev,
|
||||||
|
|
|
@ -46,6 +46,7 @@ export const createPartializedState = (state: StoreState) => ({
|
||||||
apiEndpoint: state.apiEndpoint,
|
apiEndpoint: state.apiEndpoint,
|
||||||
theme: state.theme,
|
theme: state.theme,
|
||||||
autoTitle: state.autoTitle,
|
autoTitle: state.autoTitle,
|
||||||
|
advancedMode: state.advancedMode,
|
||||||
prompts: state.prompts,
|
prompts: state.prompts,
|
||||||
defaultChatConfig: state.defaultChatConfig,
|
defaultChatConfig: state.defaultChatConfig,
|
||||||
defaultSystemMessage: state.defaultSystemMessage,
|
defaultSystemMessage: state.defaultSystemMessage,
|
||||||
|
|
Loading…
Reference in a new issue