Track change state of profile editor
This commit is contained in:
parent
f8fc158861
commit
dfe30b12bd
|
@ -1,24 +1,53 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { getProfile } from './Profiles.svelte'
|
import { getProfile } from './Profiles.svelte'
|
||||||
import { setChatSettingValue } from './Storage.svelte'
|
import { cleanSettingValue, setChatSettingValue } from './Storage.svelte'
|
||||||
import type { Chat, ChatSetting, ChatSettings } from './Types.svelte'
|
import type { Chat, ChatSetting, ChatSettings, SettingPrompt } from './Types.svelte'
|
||||||
import { autoGrowInputOnEvent } from './Util.svelte'
|
import { autoGrowInputOnEvent } from './Util.svelte'
|
||||||
|
|
||||||
export let setting:ChatSetting
|
export let setting:ChatSetting
|
||||||
export let chatSettings:ChatSettings
|
export let chatSettings:ChatSettings
|
||||||
export let chat:Chat
|
export let chat:Chat
|
||||||
export let chatDefaults:Record<string, any>
|
export let chatDefaults:Record<string, any>
|
||||||
export let defaultProfile:String
|
export let originalProfile:String
|
||||||
|
|
||||||
const chatId = chat.id
|
const chatId = chat.id
|
||||||
|
|
||||||
|
if (originalProfile) {
|
||||||
|
// eventually...
|
||||||
|
}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
const refreshSettings = () => {
|
const refreshSettings = () => {
|
||||||
dispatch('refresh')
|
dispatch('refresh')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const settingChecks:Record<string, SettingPrompt[]> = {
|
||||||
|
'profile': [
|
||||||
|
{
|
||||||
|
prompt:'Unsaved changes to the current profile will be lost.\n Continue?',
|
||||||
|
fn: (setting, newVal, oldVal) => {
|
||||||
|
return !!chatSettings.isDirty && newVal !== oldVal
|
||||||
|
},
|
||||||
|
passed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prompt:'Personality change will not correctly apply to existing chat session.\n Continue?',
|
||||||
|
fn: (setting, newVal, oldVal) => {
|
||||||
|
return chat.sessionStarted && newVal !== originalProfile &&
|
||||||
|
(getProfile(newVal).characterName !== chatSettings.characterName)
|
||||||
|
},
|
||||||
|
passed: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetSettingCheck = (key:keyof ChatSettings) => {
|
||||||
|
const checks = settingChecks[key]
|
||||||
|
checks && checks.forEach((c)=>{c.passed=false})
|
||||||
|
}
|
||||||
|
|
||||||
let debounce: any
|
let debounce: any
|
||||||
|
|
||||||
const queueSettingValueChange = (event: Event, setting: ChatSetting) => {
|
const queueSettingValueChange = (event: Event, setting: ChatSetting) => {
|
||||||
|
@ -44,26 +73,42 @@
|
||||||
const newVal = chatSettings[setting.key]
|
const newVal = chatSettings[setting.key]
|
||||||
if (val === newVal) return
|
if (val === newVal) return
|
||||||
try {
|
try {
|
||||||
(typeof setting.afterChange === 'function') && setting.afterChange(chatId, setting, chatSettings[setting.key]) &&
|
if((typeof setting.afterChange === 'function') && setting.afterChange(chatId, setting, chatSettings[setting.key])){
|
||||||
|
console.log('Refreshed from setting', setting.key, chatSettings[setting.key], val)
|
||||||
refreshSettings()
|
refreshSettings()
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setChatSettingValue(chatId, setting, val)
|
setChatSettingValue(chatId, setting, val)
|
||||||
window.alert('Unable to change:\n' + e.message)
|
window.alert('Unable to change:\n' + e.message)
|
||||||
}
|
}
|
||||||
dispatch('change', setting)
|
dispatch('change', setting)
|
||||||
}
|
}
|
||||||
if (setting.key === 'profile' && chat.sessionStarted &&
|
const checks = settingChecks[setting.key] || []
|
||||||
(getProfile(el.value).characterName !== chatSettings.characterName)) {
|
const newVal = cleanSettingValue(setting.type, el.checked||el.value)
|
||||||
const val = chatSettings[setting.key]
|
for (let i = 0, l = checks.length; i < l; i++) {
|
||||||
if (window.confirm('Personality change will not correctly apply to existing chat session.\n Continue?')) {
|
let c = checks[i]
|
||||||
doSet()
|
if(c.passed) continue
|
||||||
|
if (c.fn(setting, newVal, val)) {
|
||||||
|
// eventually this needs to be an async call to a confirmation modal where
|
||||||
|
// "passed", not really being used here, will be reworked to some other
|
||||||
|
// state to deal with inevitable issues once a non-blocking modal is used.
|
||||||
|
if (window.confirm(c.prompt)) {
|
||||||
|
c.passed = true
|
||||||
} else {
|
} else {
|
||||||
// roll-back
|
// roll-back
|
||||||
setChatSettingValue(chatId, setting, val)
|
setChatSettingValue(chatId, setting, val)
|
||||||
// refresh setting modal, if open
|
// refresh setting modal, if open
|
||||||
refreshSettings()
|
refreshSettings()
|
||||||
|
resetSettingCheck(setting.key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.passed = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// passed all
|
||||||
|
resetSettingCheck(setting.key)
|
||||||
debounce = setTimeout(doSet, 250)
|
debounce = setTimeout(doSet, 250)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { applyProfile, getDefaultProfileKey, getProfile, getProfileSelect } from './Profiles.svelte'
|
import { applyProfile, getDefaultProfileKey, getProfile, getProfileSelect } from './Profiles.svelte'
|
||||||
import { getChatDefaults, getChatSettingList, getChatSettingObjectByKey } from './Settings.svelte'
|
import { getChatDefaults, getChatSettingList, getChatSettingObjectByKey, getExcludeFromProfile } from './Settings.svelte'
|
||||||
import {
|
import {
|
||||||
saveChatStore,
|
saveChatStore,
|
||||||
apiKeyStorage,
|
apiKeyStorage,
|
||||||
|
@ -44,11 +44,14 @@
|
||||||
const settingsList = getChatSettingList()
|
const settingsList = getChatSettingList()
|
||||||
const modelSetting = getChatSettingObjectByKey('model') as ChatSetting & SettingSelect
|
const modelSetting = getChatSettingObjectByKey('model') as ChatSetting & SettingSelect
|
||||||
const chatDefaults = getChatDefaults()
|
const chatDefaults = getChatDefaults()
|
||||||
|
const excludeFromProfile = getExcludeFromProfile()
|
||||||
|
|
||||||
$: chat = $chatsStorage.find((chat) => chat.id === chatId) as Chat
|
$: chat = $chatsStorage.find((chat) => chat.id === chatId) as Chat
|
||||||
$: chatSettings = chat.settings
|
$: chatSettings = chat.settings
|
||||||
$: globalStore = $globalStorage
|
$: globalStore = $globalStorage
|
||||||
|
|
||||||
|
let originalProfile = chatSettings && chatSettings.profile
|
||||||
|
|
||||||
afterUpdate(() => {
|
afterUpdate(() => {
|
||||||
sizeTextElements()
|
sizeTextElements()
|
||||||
})
|
})
|
||||||
|
@ -66,6 +69,7 @@
|
||||||
|
|
||||||
const refreshSettings = async () => {
|
const refreshSettings = async () => {
|
||||||
showSettingsModal && showSettings()
|
showSettingsModal && showSettings()
|
||||||
|
setDirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
const cloneProfile = () => {
|
const cloneProfile = () => {
|
||||||
|
@ -197,26 +201,33 @@
|
||||||
return cname
|
return cname
|
||||||
}
|
}
|
||||||
|
|
||||||
const setDirty = (e:CustomEvent) => {
|
// excludeFromProfile
|
||||||
|
|
||||||
|
const deepEqual = (x:any, y:any) => {
|
||||||
|
const ok = Object.keys, tx = typeof x, ty = typeof y
|
||||||
|
return x && y && tx === 'object' && tx === ty ? (
|
||||||
|
ok(x).every(key => excludeFromProfile[key] || deepEqual(x[key], y[key]))
|
||||||
|
) : (x === y || ((x===undefined||x===null||x===false) && (y===undefined||y===null||y===false)))
|
||||||
|
}
|
||||||
|
|
||||||
|
const setDirty = (e:CustomEvent|undefined = undefined) => {
|
||||||
|
if (e) {
|
||||||
const setting = e.detail as ChatSetting
|
const setting = e.detail as ChatSetting
|
||||||
const key = setting.key
|
const key = setting.key
|
||||||
if (key === 'profile') return
|
if (key === 'profile') return
|
||||||
|
}
|
||||||
const profile = getProfile(chatSettings.profile)
|
const profile = getProfile(chatSettings.profile)
|
||||||
const isDirty = profile[key] !== chatSettings[key]
|
chatSettings.isDirty = !deepEqual(profile, chatSettings)
|
||||||
console.log('Is dirty?', setting, isDirty, profile[key], chatSettings[key])
|
|
||||||
chatSettings.isDirty = isDirty
|
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="modal" class:is-active={showSettingsModal}>
|
<div class="modal" class:is-active={showSettingsModal}>
|
||||||
<div class="modal-background" on:click={closeSettings} />
|
<div class="modal-background" on:click={closeSettings} />
|
||||||
<div class="modal-card" on:click={() => { showProfileMenu = false }}>
|
<div class="modal-card" on:click={() => { showProfileMenu = false }}>
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
<p class="modal-card-title">Chat Settings</p>
|
<p class="modal-card-title">Chat Settings</p>
|
||||||
|
|
||||||
<div class="dropdown is-right" class:is-active={showProfileMenu}>
|
<div class="dropdown is-right" class:is-active={showProfileMenu}>
|
||||||
<div class="dropdown-trigger">
|
<div class="dropdown-trigger">
|
||||||
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu3" on:click|preventDefault|stopPropagation={() => { showProfileMenu = !showProfileMenu }}>
|
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu3" on:click|preventDefault|stopPropagation={() => { showProfileMenu = !showProfileMenu }}>
|
||||||
|
@ -256,7 +267,7 @@
|
||||||
<section class="modal-card-body">
|
<section class="modal-card-body">
|
||||||
{#key showSettingsModal}
|
{#key showSettingsModal}
|
||||||
{#each settingsList as setting}
|
{#each settingsList as setting}
|
||||||
<ChatSettingField on:refresh={refreshSettings} on:change={setDirty} chat={chat} chatDefaults={chatDefaults} chatSettings={chatSettings} setting={setting} defaultProfile={defaultProfile} />
|
<ChatSettingField on:refresh={refreshSettings} on:change={setDirty} chat={chat} chatDefaults={chatDefaults} chatSettings={chatSettings} setting={setting} originalProfile={originalProfile} />
|
||||||
{/each}
|
{/each}
|
||||||
{/key}
|
{/key}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -100,12 +100,10 @@ const profileSetting: ChatSetting & SettingSelect = {
|
||||||
options: [], // Set by Profiles
|
options: [], // Set by Profiles
|
||||||
type: 'select',
|
type: 'select',
|
||||||
afterChange: (chatId, setting) => {
|
afterChange: (chatId, setting) => {
|
||||||
applyProfile(chatId, '', !getChat(chatId).sessionStarted)
|
applyProfile(chatId)
|
||||||
return true // Signal we should refresh the setting modal
|
return true // Signal we should refresh the setting modal
|
||||||
},
|
},
|
||||||
setDefault: (chatId, setting, value) => {
|
setDefault: (chatId, setting, value) => {},
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settings that will not be part of the API request
|
// Settings that will not be part of the API request
|
||||||
|
@ -230,7 +228,8 @@ const modelSetting: ChatSetting & SettingSelect = {
|
||||||
headerClass: 'is-warning',
|
headerClass: 'is-warning',
|
||||||
options: [],
|
options: [],
|
||||||
type: 'select',
|
type: 'select',
|
||||||
forceApi: true // Need to make sure we send this
|
forceApi: true, // Need to make sure we send this
|
||||||
|
afterChange: (chatId, setting) => true, // refresh settings
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatSettingsList: ChatSetting[] = [
|
const chatSettingsList: ChatSetting[] = [
|
||||||
|
@ -275,10 +274,9 @@ const chatSettingsList: ChatSetting[] = [
|
||||||
'The token count of your prompt plus max_tokens cannot exceed the model\'s context length. Most models have a context length of 2048 tokens (except for the newest models, which support 4096).\n',
|
'The token count of your prompt plus max_tokens cannot exceed the model\'s context length. Most models have a context length of 2048 tokens (except for the newest models, which support 4096).\n',
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 32768,
|
max: 32768,
|
||||||
step: 128,
|
step: 1,
|
||||||
type: 'number',
|
type: 'number',
|
||||||
forceApi: true, // Since default here is different than gpt default, will make sure we always send it
|
forceApi: true, // Since default here is different than gpt default, will make sure we always send it
|
||||||
afterChange: (chatId, setting) => true, // refresh settings
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'presence_penalty',
|
key: 'presence_penalty',
|
||||||
|
|
|
@ -170,4 +170,10 @@ type SettingBoolean = {
|
||||||
headerClass?: string;
|
headerClass?: string;
|
||||||
} & (SettingNumber | SettingSelect | SettingBoolean | SettingText | SettingOther);
|
} & (SettingNumber | SettingSelect | SettingBoolean | SettingText | SettingOther);
|
||||||
|
|
||||||
|
export type SettingPrompt = {
|
||||||
|
prompt: string;
|
||||||
|
fn: (setting:ChatSetting, newVal:any, oldVal:any)=>boolean;
|
||||||
|
passed: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -7,12 +7,15 @@
|
||||||
export const autoGrowInputOnEvent = (event: Event) => {
|
export const autoGrowInputOnEvent = (event: Event) => {
|
||||||
// Resize the textarea to fit the content - auto is important to reset the height after deleting content
|
// Resize the textarea to fit the content - auto is important to reset the height after deleting content
|
||||||
if (event.target === null) return
|
if (event.target === null) return
|
||||||
|
(event.target as any).__didAutoGrow = false
|
||||||
autoGrowInput(event.target as HTMLTextAreaElement)
|
autoGrowInput(event.target as HTMLTextAreaElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const autoGrowInput = (el: HTMLTextAreaElement) => {
|
export const autoGrowInput = (el: HTMLTextAreaElement) => {
|
||||||
el.style.height = '38px' // don't use "auto" here. Firefox will over-size.
|
const anyEl = el as any // Oh how I hate typescript. All the markup of Java with no real payoff..
|
||||||
|
if (!anyEl.__didAutoGrow) el.style.height = '38px' // don't use "auto" here. Firefox will over-size.
|
||||||
el.style.height = el.scrollHeight + 'px'
|
el.style.height = el.scrollHeight + 'px'
|
||||||
|
anyEl.__didAutoGrow = true // don't resize this one again unless it's via an event
|
||||||
}
|
}
|
||||||
|
|
||||||
export const scrollIntoViewWithOffset = (element:HTMLElement, offset:number) => {
|
export const scrollIntoViewWithOffset = (element:HTMLElement, offset:number) => {
|
||||||
|
|
Loading…
Reference in New Issue