Use modals instead of window.alert & confirm
This commit is contained in:
parent
db6e5898df
commit
5853b9e451
|
@ -17,11 +17,13 @@
|
|||
faEye,
|
||||
faEyeSlash
|
||||
} from '@fortawesome/free-solid-svg-icons/index'
|
||||
import { apiKeyStorage, addChatFromJSON, chatsStorage, checkStateChange, clearChats, clearMessages, copyChat, globalStorage, setGlobalSettingValueByKey, showSetChatSettings, pinMainMenu } from './Storage.svelte'
|
||||
import { apiKeyStorage, addChatFromJSON, chatsStorage, checkStateChange, clearChats, clearMessages, copyChat, globalStorage, setGlobalSettingValueByKey, showSetChatSettings, pinMainMenu, getChat, deleteChat } from './Storage.svelte'
|
||||
import { exportAsMarkdown, exportChatAsJSON } from './Export.svelte'
|
||||
import { restartProfile } from './Profiles.svelte'
|
||||
import { replace } from 'svelte-spa-router'
|
||||
import { clickOutside } from 'svelte-use-click-outside'
|
||||
import { openModal } from 'svelte-modals'
|
||||
import PromptConfirm from './PromptConfirm.svelte'
|
||||
|
||||
export let chatId
|
||||
export const show = (showHide:boolean = true) => {
|
||||
|
@ -29,6 +31,8 @@
|
|||
}
|
||||
export let style: string = 'is-right'
|
||||
|
||||
$: sortedChats = $chatsStorage.sort((a, b) => b.id - a.id)
|
||||
|
||||
let showChatMenu = false
|
||||
let chatFileInput
|
||||
|
||||
|
@ -43,20 +47,45 @@
|
|||
}
|
||||
}
|
||||
|
||||
const deleteChat = () => {
|
||||
const delChat = () => {
|
||||
close()
|
||||
if (window.confirm('Are you sure you want to delete this chat?')) {
|
||||
replace('/').then(() => {
|
||||
chatsStorage.update((chats) => chats.filter((chat) => chat.id !== chatId))
|
||||
})
|
||||
openModal(PromptConfirm, {
|
||||
title: 'Delete Chat',
|
||||
message: 'Are you sure you want to delete this chat?',
|
||||
class: 'is-warning',
|
||||
confirmButtonClass: 'is-warning',
|
||||
confirmButton: 'Delete Chat',
|
||||
onConfirm: () => {
|
||||
const thisChat = getChat(chatId)
|
||||
const thisIndex = sortedChats.indexOf(thisChat)
|
||||
const prevChat = sortedChats[thisIndex - 1]
|
||||
const nextChat = sortedChats[thisIndex + 1]
|
||||
const newChat = nextChat || prevChat
|
||||
if (!newChat) {
|
||||
// No other chats, clear all and go to home
|
||||
replace('/').then(() => { deleteChat(chatId) })
|
||||
} else {
|
||||
// Delete the current chat and go to the max chatId
|
||||
replace(`/chat/${newChat.id}`).then(() => { deleteChat(chatId) })
|
||||
}
|
||||
},
|
||||
onCancel: () => {}
|
||||
})
|
||||
}
|
||||
|
||||
const confirmClearChats = () => {
|
||||
close()
|
||||
if (window.confirm('Are you sure you want to delete ALL of your chats?')) {
|
||||
openModal(PromptConfirm, {
|
||||
title: 'Delete ALL Chat',
|
||||
message: 'Are you sure you want to delete ALL of your chats?',
|
||||
class: 'is-danger',
|
||||
confirmButtonClass: 'is-danger',
|
||||
confirmButton: 'Delete ALL',
|
||||
onConfirm: () => {
|
||||
clearChats()
|
||||
}
|
||||
},
|
||||
onCancel: () => {}
|
||||
})
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
|
@ -116,7 +145,7 @@
|
|||
<span class="menu-icon"><Fa icon={faFileExport}/></span> Export Chat Markdown
|
||||
</a>
|
||||
<hr class="dropdown-divider">
|
||||
<a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={() => { if (chatId) close(); deleteChat() }}>
|
||||
<a href={'#'} class="dropdown-item" class:is-disabled={!chatId} on:click|preventDefault={() => { if (chatId) close(); delChat() }}>
|
||||
<span class="menu-icon"><Fa icon={faTrash}/></span> Delete Chat
|
||||
</a>
|
||||
<a href={'#'} class="dropdown-item" on:click|preventDefault={() => { if (chatId) confirmClearChats() }}>
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
// import { getProfile } from './Profiles.svelte'
|
||||
import { cleanSettingValue, setChatSettingValue } from './Storage.svelte'
|
||||
import type { Chat, ChatSetting, ChatSettings, ControlAction, FieldControl, SettingPrompt } from './Types.svelte'
|
||||
import { autoGrowInputOnEvent } from './Util.svelte'
|
||||
import { autoGrowInputOnEvent, errorNotice } from './Util.svelte'
|
||||
// import { replace } from 'svelte-spa-router'
|
||||
import Fa from 'svelte-fa/src/fa.svelte'
|
||||
import { openModal } from 'svelte-modals'
|
||||
import PromptConfirm from './PromptConfirm.svelte'
|
||||
import PromptNotice from './PromptNotice.svelte'
|
||||
|
||||
export let setting:ChatSetting
|
||||
export let chatSettings:ChatSettings
|
||||
|
@ -87,7 +88,7 @@
|
|||
(typeof setting.beforeChange === 'function') && setting.beforeChange(chatId, setting, el.checked || el.value) &&
|
||||
refreshSettings()
|
||||
} catch (e) {
|
||||
window.alert('Unable to change:\n' + e.message)
|
||||
openModal(PromptNotice, errorNotice('Unable to change:', e))
|
||||
}
|
||||
switch (setting.type) {
|
||||
case 'boolean':
|
||||
|
@ -106,7 +107,7 @@
|
|||
}
|
||||
} catch (e) {
|
||||
setChatSettingValue(chatId, setting, val)
|
||||
window.alert('Unable to change:\n' + e.message)
|
||||
openModal(PromptNotice, errorNotice('Unable to change:', e))
|
||||
}
|
||||
dispatch('change', setting)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
} from './Storage.svelte'
|
||||
import { supportedModels, type Chat, type ChatSetting, type ResponseModels, type SettingSelect, type SelectOption, type ChatSettings } from './Types.svelte'
|
||||
import { sizeTextElements } from './Util.svelte'
|
||||
import { errorNotice, sizeTextElements } from './Util.svelte'
|
||||
import Fa from 'svelte-fa/src/fa.svelte'
|
||||
import {
|
||||
faTrash,
|
||||
|
@ -33,6 +33,8 @@
|
|||
import ChatSettingField from './ChatSettingField.svelte'
|
||||
import { getModelMaxTokens } from './Stats.svelte'
|
||||
import { replace } from 'svelte-spa-router'
|
||||
import { openModal } from 'svelte-modals'
|
||||
import PromptNotice from './PromptNotice.svelte'
|
||||
|
||||
export let chatId:number
|
||||
export const show = () => { showSettings() }
|
||||
|
@ -101,7 +103,7 @@
|
|||
applyProfile(chatId, clone.profile)
|
||||
refreshSettings()
|
||||
} catch (e) {
|
||||
window.alert('Error cloning profile: \n' + e.message)
|
||||
openModal(PromptNotice, errorNotice('Error cloning profile:', e))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +117,7 @@
|
|||
applyProfile(chatId, chat.settings.profile as any)
|
||||
refreshSettings()
|
||||
} catch (e) {
|
||||
window.alert('Error deleting profile: \n' + e.message)
|
||||
openModal(PromptNotice, errorNotice('Error deleting profile:', e))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,7 +140,7 @@
|
|||
saveCustomProfile(profile)
|
||||
refreshSettings()
|
||||
} catch (e) {
|
||||
window.alert('Unable to import profile: \n' + e.message)
|
||||
openModal(PromptNotice, errorNotice('Unable to import profile:', e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,7 +203,7 @@
|
|||
saveCustomProfile(chat.settings)
|
||||
refreshSettings()
|
||||
} catch (e) {
|
||||
window.alert('Error saving profile: \n' + e.message)
|
||||
openModal(PromptNotice, errorNotice('Error saving profile:', e))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
import type { Message, Model, Chat } from './Types.svelte'
|
||||
import Fa from 'svelte-fa/src/fa.svelte'
|
||||
import { faTrash, faDiagramPredecessor, faDiagramNext, faCircleCheck, faPaperPlane, faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons/index'
|
||||
import { scrollIntoViewWithOffset } from './Util.svelte'
|
||||
import { errorNotice, scrollIntoViewWithOffset } from './Util.svelte'
|
||||
import { openModal } from 'svelte-modals'
|
||||
import PromptConfirm from './PromptConfirm.svelte'
|
||||
import PromptNotice from './PromptNotice.svelte'
|
||||
|
||||
export let message:Message
|
||||
export let chatId:number
|
||||
|
@ -115,24 +118,33 @@
|
|||
waitingForDeleteConfirm = 0
|
||||
if (message.summarized) {
|
||||
// is in a summary, so we're summarized
|
||||
window.alert('Sorry, you can\'t delete a summarized message')
|
||||
openModal(PromptNotice, errorNotice('Sorry, you can\'t delete a summarized message'))
|
||||
return
|
||||
}
|
||||
if (message.summary) {
|
||||
// We're linked to messages we're a summary of
|
||||
if (window.confirm('Are you sure you want to delete this summary?\nYour session may be too long to submit again after you do.')) {
|
||||
openModal(PromptConfirm, {
|
||||
title: 'Delete Summary',
|
||||
message: '<p>Are you sure you want to delete this summary?</p><p>Your session may be too long to submit again after you do.</p>',
|
||||
asHtml: true,
|
||||
class: 'is-warning',
|
||||
confirmButtonClass: 'is-warning',
|
||||
confirmButton: 'Delete Summary',
|
||||
onConfirm: () => {
|
||||
try {
|
||||
deleteSummaryMessage(chatId, message.uuid)
|
||||
} catch (e) {
|
||||
window.alert('Unable to delete summary:\n' + e.message)
|
||||
}
|
||||
}
|
||||
return
|
||||
openModal(PromptNotice, errorNotice('Unable to delete summary:', e))
|
||||
}
|
||||
},
|
||||
onCancel: () => {}
|
||||
})
|
||||
} else {
|
||||
try {
|
||||
deleteMessage(chatId, message.uuid)
|
||||
} catch (e) {
|
||||
window.alert('Unable to delete:\n' + e.message)
|
||||
openModal(PromptNotice, errorNotice('Unable to delete:', e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,21 +162,21 @@
|
|||
waitingForTruncateConfirm = 0
|
||||
if (message.summarized) {
|
||||
// is in a summary, so we're summarized
|
||||
window.alert('Sorry, you can\'t truncate a summarized message')
|
||||
openModal(PromptNotice, errorNotice('Sorry, you can\'t truncate a summarized message'))
|
||||
return
|
||||
}
|
||||
try {
|
||||
truncateFromMessage(chatId, message.uuid)
|
||||
$submitExitingPromptsNow = true
|
||||
} catch (e) {
|
||||
window.alert('Unable to delete:\n' + e.message)
|
||||
openModal(PromptNotice, errorNotice('Unable to delete:', e))
|
||||
}
|
||||
}
|
||||
|
||||
const setSuppress = (value:boolean) => {
|
||||
if (message.summarized) {
|
||||
// is in a summary, so we're summarized
|
||||
window.alert('Sorry, you can\'t suppress a summarized message')
|
||||
openModal(PromptNotice, errorNotice('Sorry, you can\'t suppress a summarized message'))
|
||||
return
|
||||
}
|
||||
message.suppress = value
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<script lang="ts">
|
||||
import { closeModal } from 'svelte-modals'
|
||||
|
||||
export let isOpen:boolean
|
||||
|
||||
export let title:string
|
||||
export let message:string
|
||||
export let asHtml:boolean = false
|
||||
|
||||
export let onConfirm:()=>boolean|void
|
||||
|
||||
export let confirmButton:string = 'Okay'
|
||||
export let confirmButtonClass:string = 'is-info'
|
||||
let classes:string = ''
|
||||
export { classes as class }
|
||||
|
||||
const doConfirm = () => {
|
||||
if (!onConfirm || !onConfirm()) closeModal()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if isOpen}
|
||||
<div class="modal is-active" on:modal-esc={doConfirm}>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="modal-background" on:click={doConfirm} />
|
||||
<div class="modal-content nomax">
|
||||
<article class="message {classes}">
|
||||
<div class="message-header">
|
||||
<p>{title}</p>
|
||||
<button class="delete" aria-label="close" type="button" on:click={doConfirm}></button>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
{#if asHtml}{@html message}{:else}{message}{/if}
|
||||
</div>
|
||||
<div class="message-footer">
|
||||
<div class="level is-mobile">
|
||||
<div class="level-right">
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
<button class="button {confirmButtonClass}" type="button" on:click={doConfirm} >{confirmButton}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
|
@ -18,7 +18,7 @@ import {
|
|||
|
||||
} from './Types.svelte'
|
||||
|
||||
export const defaultModel:Model = 'gpt-3.5-turbo-0301'
|
||||
export const defaultModel:Model = 'gpt-3.5-turbo'
|
||||
|
||||
export const getChatSettingList = (): ChatSetting[] => {
|
||||
return chatSettingsList
|
||||
|
@ -86,6 +86,8 @@ const defaults:ChatSettings = {
|
|||
systemPrompt: '',
|
||||
autoStartSession: false,
|
||||
trainingPrompts: [],
|
||||
// useResponseAlteration: false,
|
||||
// responseAlterations: [],
|
||||
isDirty: false
|
||||
}
|
||||
|
||||
|
@ -129,8 +131,7 @@ const profileSetting: ChatSetting & SettingSelect = {
|
|||
}
|
||||
|
||||
// Settings that will not be part of the API request
|
||||
const nonRequestSettings: ChatSetting[] = [
|
||||
profileSetting,
|
||||
const systemPromptSettings: ChatSetting[] = [
|
||||
{
|
||||
key: 'profileName',
|
||||
name: 'Profile Name',
|
||||
|
@ -181,7 +182,10 @@ const nonRequestSettings: ChatSetting[] = [
|
|||
title: 'If possible, auto-start the chat session, sending a system prompt to get an initial response.',
|
||||
type: 'boolean',
|
||||
hide: (chatId) => !getChatSettings(chatId).useSystemPrompt
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
const summarySettings: ChatSetting[] = [
|
||||
{
|
||||
key: 'useSummarization',
|
||||
name: 'Enable Continuous Chat',
|
||||
|
@ -242,6 +246,54 @@ const nonRequestSettings: ChatSetting[] = [
|
|||
}
|
||||
]
|
||||
|
||||
// const responseAlterationSettings: ChatSetting[] = [
|
||||
// {
|
||||
// key: 'useResponseAlteration',
|
||||
// name: 'Alter Responses',
|
||||
// header: 'Automatic Response Alteration',
|
||||
// headerClass: 'is-info',
|
||||
// title: 'When an undesired response is encountered, try to alter it in effort to improve future responses.',
|
||||
// type: 'boolean',
|
||||
// hide: () => true
|
||||
// },
|
||||
// {
|
||||
// key: 'responseAlterations',
|
||||
// name: 'Alterations',
|
||||
// title: 'Add find/replace or re-prompts.',
|
||||
// header: 'Profile / Presets',
|
||||
// headerClass: 'is-info',
|
||||
// settings: [
|
||||
// {
|
||||
// key: 'type',
|
||||
// type: 'select',
|
||||
// name: 'Alteration Type',
|
||||
// default: 'replace',
|
||||
// options: [{
|
||||
// value: 'replace',
|
||||
// text: 'Regexp Find / Replace'
|
||||
// }, {
|
||||
// value: 'prompt',
|
||||
// text: 'Re-prompt with Instructions'
|
||||
// }]
|
||||
// },
|
||||
// {
|
||||
// key: 'match',
|
||||
// type: 'text',
|
||||
// name: 'Match Expression',
|
||||
// title: 'Regular expression used to match '
|
||||
// },
|
||||
// {
|
||||
// key: 'replace',
|
||||
// type: 'text',
|
||||
// name: 'Alteration',
|
||||
// title: 'Regexp Replacement or Re-prompt'
|
||||
// }
|
||||
// ],
|
||||
// type: 'subset',
|
||||
// hide: (chatId) => !getChatSettings(chatId).useResponseAlteration!
|
||||
// }
|
||||
// ]
|
||||
|
||||
const modelSetting: ChatSetting & SettingSelect = {
|
||||
key: 'model',
|
||||
name: 'Model',
|
||||
|
@ -255,7 +307,10 @@ const modelSetting: ChatSetting & SettingSelect = {
|
|||
}
|
||||
|
||||
const chatSettingsList: ChatSetting[] = [
|
||||
...nonRequestSettings,
|
||||
profileSetting,
|
||||
...systemPromptSettings,
|
||||
...summarySettings,
|
||||
// ...responseAlterationSettings,
|
||||
modelSetting,
|
||||
{
|
||||
key: 'temperature',
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
import { getChatSettingObjectByKey, getGlobalSettingObjectByKey, getChatDefaults, getExcludeFromProfile } from './Settings.svelte'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { getProfile, getProfiles, isStaticProfile, newNameForProfile, restartProfile } from './Profiles.svelte'
|
||||
import { openModal } from 'svelte-modals'
|
||||
import PromptNotice from './PromptNotice.svelte'
|
||||
import { errorNotice } from './Util.svelte'
|
||||
|
||||
export const chatsStorage = persisted('chats', [] as Chat[])
|
||||
export const globalStorage = persisted('global', {} as GlobalSettings)
|
||||
|
@ -56,11 +59,11 @@
|
|||
try {
|
||||
chat = JSON.parse(json) as Chat
|
||||
if (!chat.settings || !chat.messages || isNaN(chat.id)) {
|
||||
window.alert('Not valid Chat JSON')
|
||||
openModal(PromptNotice, errorNotice('Not valid Chat JSON'))
|
||||
return 0
|
||||
}
|
||||
} catch (err) {
|
||||
window.alert("Can't parse file JSON")
|
||||
openModal(PromptNotice, errorNotice("Can't parse file JSON"))
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,12 @@
|
|||
suppress?: boolean;
|
||||
};
|
||||
|
||||
export type ResponseAlteration = {
|
||||
type: 'prompt' | 'replace';
|
||||
match: string;
|
||||
replace: string;
|
||||
}
|
||||
|
||||
export type Request = {
|
||||
model?: Model;
|
||||
messages?: Message[];
|
||||
|
@ -59,6 +65,8 @@
|
|||
systemPrompt: string;
|
||||
autoStartSession: boolean;
|
||||
trainingPrompts?: Message[];
|
||||
useResponseAlteration?: boolean;
|
||||
responseAlterations?: ResponseAlteration[];
|
||||
isDirty?: boolean;
|
||||
} & Request;
|
||||
|
||||
|
@ -157,6 +165,11 @@ type SettingBoolean = {
|
|||
getAction: (chatId:number, setting:any, value:any) => ControlAction;
|
||||
};
|
||||
|
||||
export type SubSetting = {
|
||||
type: 'subset';
|
||||
settings: any[];
|
||||
};
|
||||
|
||||
export type ChatSetting = {
|
||||
key: keyof ChatSettings;
|
||||
name: string;
|
||||
|
@ -171,7 +184,8 @@ type SettingBoolean = {
|
|||
fieldControls?: FieldControl[];
|
||||
beforeChange?: (chatId:number, setting:ChatSetting, value:any) => boolean;
|
||||
afterChange?: (chatId:number, setting:ChatSetting, value:any) => boolean;
|
||||
} & (SettingNumber | SettingSelect | SettingBoolean | SettingText | SettingTextArea | SettingOther);
|
||||
} & (SettingNumber | SettingSelect | SettingBoolean | SettingText | SettingTextArea | SettingOther | SubSetting);
|
||||
|
||||
|
||||
export type GlobalSetting = {
|
||||
key: keyof GlobalSettings;
|
||||
|
|
|
@ -52,4 +52,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
export const errorNotice = (message:string, error:Error|undefined = undefined):any => {
|
||||
return {
|
||||
title: 'Error',
|
||||
class: 'is-danger',
|
||||
message: message + (error ? '<br>' + error.message : ''),
|
||||
asHtml: true,
|
||||
onConfirm: () => {}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
Loading…
Reference in New Issue