// TODO refactor import React, { FC, PropsWithChildren, ReactElement, useState } from 'react'; import { Button, Dialog, DialogBody, DialogContent, DialogSurface, DialogTrigger, Input, Switch, Tab, TabList, Text } from '@fluentui/react-components'; import { Accessibility28Regular, Chat20Regular, ClipboardEdit20Regular, Delete20Regular, Dismiss20Regular, Edit20Regular, Globe20Regular } from '@fluentui/react-icons'; import { ToolTipButton } from '../../components/ToolTipButton'; import { useTranslation } from 'react-i18next'; import { botName, Conversation, ConversationMessage, MessageType, userName } from '../Chat'; import { SelectTabEventHandler } from '@fluentui/react-tabs'; import { Labeled } from '../../components/Labeled'; import commonStore from '../../stores/commonStore'; import logo from '../../assets/images/logo.jpg'; import { observer } from 'mobx-react-lite'; import { MessagesEditor } from './MessagesEditor'; import { ClipboardGetText, ClipboardSetText } from '../../../wailsjs/runtime'; import { toast } from 'react-toastify'; import { CustomToastContainer } from '../../components/CustomToastContainer'; import { v4 as uuid } from 'uuid'; export type PresetType = 'chat' | 'completion' | 'chatInCompletion' export type Preset = { name: string, tag: string, // if name and sourceUrl are same, it will be overridden when importing sourceUrl: string, desc: string, avatarImg: string, type: PresetType, // chat welcomeMessage: string, messages: ConversationMessage[], displayPresetMessages: boolean, // completion prompt: string, stop: string, injectStart: string, injectEnd: string, } export const defaultPreset: Preset = { name: 'RWKV', tag: 'default', sourceUrl: '', desc: '', avatarImg: logo, type: 'chat', welcomeMessage: '', displayPresetMessages: true, messages: [], prompt: '', stop: '', injectStart: '', injectEnd: '' }; const setActivePreset = (preset: Preset) => { commonStore.setActivePreset(preset); //TODO if (preset.displayPresetMessages) { const conversation: Conversation = {}; const conversationOrder: string[] = []; for (const message of preset.messages) { const newUuid = uuid(); conversationOrder.push(newUuid); conversation[newUuid] = { sender: message.role === 'user' ? userName : botName, type: MessageType.Normal, color: message.role === 'user' ? 'brand' : 'colorful', time: new Date().toISOString(), content: message.content, side: message.role === 'user' ? 'right' : 'left', done: true }; } commonStore.setConversation(conversation); commonStore.setConversationOrder(conversationOrder); //} }; export const PresetCardFrame: FC void }> = (props) => { return ; }; export const PresetCard: FC<{ avatarImg: string, name: string, desc: string, tag: string, editable: boolean, presetIndex: number, onClick?: () => void }> = observer(({ avatarImg, name, desc, tag, editable, presetIndex, onClick }) => { const { t } = useTranslation(); return {name} {desc}
{t(tag)}
{editable ? } onClick={() => { commonStore.setEditingPreset({ ...commonStore.presets[presetIndex] }); }} /> } /> :
}
; }); export const ChatPresetEditor: FC<{ triggerButton: ReactElement, presetIndex: number }> = observer(({ triggerButton, presetIndex }) => { const { t } = useTranslation(); const [editingMessages, setEditingMessages] = useState(false); if (!commonStore.editingPreset) commonStore.setEditingPreset({ ...defaultPreset }); const editingPreset = commonStore.editingPreset!; const setEditingPreset = (newParams: Partial) => { commonStore.setEditingPreset({ ...editingPreset, ...newParams }); }; const importPreset = () => { ClipboardGetText().then((text) => { try { const preset = JSON.parse(text); setEditingPreset(preset); toast(t('Imported successfully'), { type: 'success', autoClose: 1000 }); } catch (e) { toast(t('Failed to import. Please copy a preset to the clipboard.'), { type: 'error', autoClose: 2500 }); } }).catch(() => { toast(t('Clipboard is empty.'), { type: 'info', autoClose: 1000 }); }); }; const copyPreset = () => { ClipboardSetText(JSON.stringify(editingPreset)).then((success) => { if (success) toast(t('Successfully copied to clipboard.'), { type: 'success', autoClose: 1000 }); }); }; const savePreset = () => { if (presetIndex === -1) { commonStore.setPresets([...commonStore.presets, { ...editingPreset }]); setEditingPreset(defaultPreset); } else { commonStore.presets[presetIndex] = editingPreset; commonStore.setPresets(commonStore.presets); } }; const activatePreset = () => { savePreset(); setActivePreset(editingPreset); }; const deletePreset = () => { commonStore.presets.splice(presetIndex, 1); commonStore.setPresets(commonStore.presets); }; return {triggerButton}
{ presetIndex === -1 ?
:
{ setEditingPreset({ name: data.value }); }} />
} /> { editingMessages ? :
{ setEditingPreset({ desc: data.value }); }} /> } /> { setEditingPreset({ avatarImg: data.value }); }} /> } /> { setEditingPreset({ welcomeMessage: data.value }); }} /> } /> { setEditingPreset({ displayPresetMessages: data.checked }); }} /> } /> { setEditingPreset({ tag: data.value }); }} /> } />
}
; }); export const ChatPresets: FC = observer(() => { const { t } = useTranslation(); return
{t('New Preset')}
} /> {/*TODO */} {/*
*/} {/* {t('Import')}*/} {/*
*/} {/*
*/} { setActivePreset(defaultPreset); }} avatarImg={defaultPreset.avatarImg} name={defaultPreset.name} desc={defaultPreset.desc} tag={defaultPreset.tag} /> {commonStore.presets.map((preset, index) => { return { setActivePreset(preset); }} key={index} avatarImg={preset.avatarImg} name={preset.name} desc={preset.desc} tag={preset.tag} />; })}
; }); type PresetsNavigationItem = { icon: ReactElement; element: ReactElement; }; const pages: { [label: string]: PresetsNavigationItem } = { Chat: { icon: , element: }, Completion: { icon: , element:
In Development
}, Online: { icon: , element:
In Development
} }; export const PresetsManager: FC<{ initTab: string }> = ({ initTab }) => { const { t } = useTranslation(); const [tab, setTab] = useState(initTab); const selectTab: SelectTabEventHandler = (e, data) => typeof data.value === 'string' ? setTab(data.value) : null; return
{Object.entries(pages).map(([label, { icon }]) => ( {t(label)} ))}
{pages[tab].element}
; }; export const PresetsButton: FC<{ tab: string, size?: 'small' | 'medium' | 'large', shape?: 'rounded' | 'circular' | 'square'; appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent'; }> = ({ tab, size, shape, appearance }) => { const { t } = useTranslation(); return } /> ; };