// TODO refactor import React, { FC, lazy, PropsWithChildren, ReactElement, useState } from 'react'; import { Button, Dialog, DialogActions, 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 { SelectTabEventHandler } from '@fluentui/react-tabs'; import { Labeled } from '../../components/Labeled'; import commonStore from '../../stores/commonStore'; import logo from '../../assets/images/logo.png'; import { observer } from 'mobx-react-lite'; import { ClipboardGetText, ClipboardSetText } from '../../../wailsjs/runtime'; import { toast } from 'react-toastify'; import { CustomToastContainer } from '../../components/CustomToastContainer'; import { absPathAsset, setActivePreset } from '../../utils'; import { Preset, PresetsNavigationItem } from '../../types/presets'; import { LazyImportComponent } from '../../components/LazyImportComponent'; const defaultPreset: Preset = { name: 'RWKV', tag: 'default', sourceUrl: '', desc: '', avatarImg: logo, type: 'chat', welcomeMessage: '', displayPresetMessages: true, messages: [], prompt: '', stop: '', injectStart: '', injectEnd: '', presystem: false, userName: '', assistantName: '' }; const MessagesEditor = lazy(() => import('./MessagesEditor')); const PresetCardFrame: FC highlight?: boolean }> = (props) => { return ; }; const PresetCard: FC<{ editable: boolean, preset: Preset, presetIndex: number, }> = observer(({ editable, preset, presetIndex }) => { const { t } = useTranslation(); return { if (e.currentTarget.contains(e.target as Node)) setActivePreset(presetIndex === -1 ? defaultPreset : preset, presetIndex); }}> {preset.name} {preset.desc}
{t(preset.tag)}
{editable ? } onClick={(e) => { e.stopPropagation(); commonStore.setEditingPreset({ ...commonStore.presets[presetIndex] }); }} /> } /> :
}
; }); const ChatPresetEditor: FC<{ triggerButton: ReactElement, presetIndex: number }> = observer(({ triggerButton, presetIndex }) => { const { t } = useTranslation(); const [open, setOpen] = React.useState(false); const [showExitConfirm, setShowExitConfirm] = React.useState(false); const [editingMessages, setEditingMessages] = useState(false); if (open && !commonStore.editingPreset) commonStore.setEditingPreset({ ...defaultPreset }); const editingPreset = commonStore.editingPreset!; const setEditingPreset = (newParams: Partial) => { commonStore.setEditingPreset({ ...editingPreset, ...newParams }); }; const importPreset = () => { ClipboardGetText().then((text) => { try { if (!text.trim().startsWith('{')) text = new TextDecoder().decode( new Uint8Array(atob(text) .split('') .map((c) => c.charCodeAt(0)))); const preset = JSON.parse(text); setEditingPreset(preset); setEditingMessages(false); 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 = () => { setOpen(false); setShowExitConfirm(false); if (presetIndex === -1) { commonStore.setPresets([...commonStore.presets, { ...editingPreset }]); } else { commonStore.presets[presetIndex] = editingPreset; commonStore.setPresets(commonStore.presets); } commonStore.setEditingPreset(null); }; const activatePreset = () => { savePreset(); setActivePreset(editingPreset, presetIndex === -1 ? commonStore.presets.length - 1 : presetIndex); }; const deletePreset = () => { if (commonStore.activePresetIndex === presetIndex) { setActivePreset(defaultPreset, -1); } commonStore.presets.splice(presetIndex, 1); commonStore.setPresets(commonStore.presets); setOpen(false); setShowExitConfirm(false); commonStore.setEditingPreset(null); }; return { if (data.open) { setOpen(true); } else if (!commonStore.editingPreset || (presetIndex === -1 && JSON.stringify(editingPreset) === JSON.stringify(defaultPreset)) || (presetIndex !== -1 && JSON.stringify(editingPreset) === JSON.stringify(commonStore.presets[presetIndex]))) { setOpen(false); setShowExitConfirm(false); commonStore.setEditingPreset(null); } else { setShowExitConfirm(true); } }}> {triggerButton} {editingPreset && {t('Content has been changed, are you sure you want to exit without saving?')}
{ presetIndex === -1 ?
:
{ setEditingPreset({ name: data.value }); }} />
} /> { editingMessages ?
{ setEditingPreset({ presystem: data.checked }); }} /> } /> { setEditingPreset({ userName: data.value }); }} /> } /> { setEditingPreset({ assistantName: data.value }); }} /> } />
:
{ setEditingPreset({ desc: data.value }); }} /> } /> { setEditingPreset({ avatarImg: data.value }); }} /> } /> { setEditingPreset({ welcomeMessage: data.value }); }} /> } /> { setEditingPreset({ displayPresetMessages: data.checked }); }} /> } /> { setEditingPreset({ tag: data.value }); }} /> } />
}
}
; }); const ChatPresets: FC = observer(() => { const { t } = useTranslation(); return
{t('New Preset')}
} /> {/*TODO */} {/*
*/} {/* {t('Import')}*/} {/*
*/} {/*
*/} {commonStore.presets.map((preset, index) => { return ; })}
; }); const pages: { [label: string]: PresetsNavigationItem } = { Chat: { icon: , element: }, Completion: { icon: , element:
In Development
}, Online: { icon: , element:
In Development
} }; 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 } /> ; };