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
	
	 Webifi
						Webifi