Simplify switching profiles. Less prompting.
This commit is contained in:
		
							parent
							
								
									759eb35bc6
								
							
						
					
					
						commit
						49c7570952
					
				|  | @ -1,10 +1,10 @@ | |||
| <script lang="ts"> | ||||
|   import { createEventDispatcher } from 'svelte' | ||||
|   import { getProfile } from './Profiles.svelte' | ||||
|   import { addChat, cleanSettingValue, setChatSettingValue } from './Storage.svelte' | ||||
|   // 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 { replace } from 'svelte-spa-router' | ||||
|   // import { replace } from 'svelte-spa-router' | ||||
|   import Fa from 'svelte-fa/src/fa.svelte' | ||||
| 
 | ||||
|   export let setting:ChatSetting | ||||
|  | @ -37,33 +37,33 @@ | |||
|           return !!chatSettings.isDirty && newVal !== oldVal | ||||
|         }, | ||||
|         passed: false | ||||
|       }, | ||||
|       { | ||||
|         prompt: 'Would you like to start a new chat session with this profile?', | ||||
|         checkPrompt: (setting, newVal, oldVal) => { | ||||
|           return chat.sessionStarted && newVal !== originalProfile && | ||||
|             (getProfile(newVal).characterName !== chatSettings.characterName) | ||||
|         }, | ||||
|         onYes: (setting, newVal, oldVal) => { | ||||
|           // start new char session, apply this profile, amd start it | ||||
|           setChatSettingValue(chatId, setting, oldVal) | ||||
|           const profile = getProfile(newVal) | ||||
|           const newChatId = addChat(profile) | ||||
|           replace(`/chat/${newChatId}`) | ||||
|           return true | ||||
|         }, | ||||
|         onNo: (setting, newVal, oldVal) => true, // Continue on no | ||||
|         passed: false | ||||
|       }, | ||||
|       { | ||||
|         prompt: 'Personality change will not correctly apply to existing chat session.\n Continue?', | ||||
|         checkPrompt: (setting, newVal, oldVal) => { | ||||
|           return chat.sessionStarted && newVal !== originalProfile && | ||||
|             (getProfile(newVal).characterName !== chatSettings.characterName) | ||||
|         }, | ||||
|         onYes: (setting, newVal, oldVal) => true, | ||||
|         passed: false | ||||
|       } | ||||
|       // { | ||||
|       //   prompt: 'Would you like to start a new chat session with this profile?', | ||||
|       //   checkPrompt: (setting, newVal, oldVal) => { | ||||
|       //     return chat.sessionStarted && newVal !== originalProfile && | ||||
|       //       (getProfile(newVal).characterName !== chatSettings.characterName) | ||||
|       //   }, | ||||
|       //   onYes: (setting, newVal, oldVal) => { | ||||
|       //     // start new char session, apply this profile, amd start it | ||||
|       //     setChatSettingValue(chatId, setting, oldVal) | ||||
|       //     const profile = getProfile(newVal) | ||||
|       //     const newChatId = addChat(profile) | ||||
|       //     replace(`/chat/${newChatId}`) | ||||
|       //     return true | ||||
|       //   }, | ||||
|       //   onNo: (setting, newVal, oldVal) => true, // Continue on no | ||||
|       //   passed: false | ||||
|       // }, | ||||
|       // { | ||||
|       //   prompt: 'Personality change will not correctly apply to existing chat session.\n Continue?', | ||||
|       //   checkPrompt: (setting, newVal, oldVal) => { | ||||
|       //     return chat.sessionStarted && newVal !== originalProfile && | ||||
|       //       (getProfile(newVal).characterName !== chatSettings.characterName) | ||||
|       //   }, | ||||
|       //   onYes: (setting, newVal, oldVal) => true, | ||||
|       //   passed: false | ||||
|       // } | ||||
|     ] | ||||
|   } | ||||
| 
 | ||||
|  | @ -98,7 +98,7 @@ | |||
|       if (val === newVal) return | ||||
|       try { | ||||
|         if ((typeof setting.afterChange === 'function') && setting.afterChange(chatId, setting, chatSettings[setting.key])) { | ||||
|           console.log('Refreshed from setting', setting.key, chatSettings[setting.key], val) | ||||
|           // console.log('Refreshed from setting', setting.key, chatSettings[setting.key], val) | ||||
|           refreshSettings() | ||||
|         } | ||||
|       } catch (e) { | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
|     addChat | ||||
| 
 | ||||
|   } from './Storage.svelte' | ||||
|   import { supportedModels, type Chat, type ChatSetting, type ResponseModels, type SettingSelect, type SelectOption } from './Types.svelte' | ||||
|   import { supportedModels, type Chat, type ChatSetting, type ResponseModels, type SettingSelect, type SelectOption, type ChatSettings } from './Types.svelte' | ||||
|   import { sizeTextElements } from './Util.svelte' | ||||
|   import Fa from 'svelte-fa/src/fa.svelte' | ||||
|   import { | ||||
|  | @ -29,7 +29,7 @@ | |||
|     faSquarePlus | ||||
|   } from '@fortawesome/free-solid-svg-icons/index' | ||||
|   import { exportProfileAsJSON } from './Export.svelte' | ||||
|   import { afterUpdate } from 'svelte' | ||||
|   import { onMount, afterUpdate } from 'svelte' | ||||
|   import ChatSettingField from './ChatSettingField.svelte' | ||||
|   import { getModelMaxTokens } from './Stats.svelte' | ||||
|   import { replace } from 'svelte-spa-router' | ||||
|  | @ -55,13 +55,25 @@ | |||
|   $: chatSettings = chat.settings | ||||
|   $: globalStore = $globalStorage | ||||
| 
 | ||||
|   const originalProfile = chatSettings && chatSettings.profile | ||||
|   let originalProfile:string | ||||
|   let originalSettings:ChatSettings | ||||
| 
 | ||||
|   onMount(async () => { | ||||
|     originalProfile = chatSettings && chatSettings.profile | ||||
|     originalSettings = chatSettings && JSON.parse(JSON.stringify(chatSettings)) | ||||
|   }) | ||||
| 
 | ||||
|   afterUpdate(() => { | ||||
|     if (!originalProfile) { | ||||
|       originalProfile = chatSettings && chatSettings.profile | ||||
|       originalSettings = chatSettings && JSON.parse(JSON.stringify(chatSettings)) | ||||
|     } | ||||
|     sizeTextElements() | ||||
|   }) | ||||
|    | ||||
|   const closeSettings = () => { | ||||
|     originalProfile = '' | ||||
|     originalSettings = {} as ChatSettings | ||||
|     showProfileMenu = false | ||||
|     $checkStateChange++ | ||||
|     showSettingsModal = 0 | ||||
|  | @ -207,12 +219,18 @@ | |||
|   } | ||||
| 
 | ||||
|   const startNewChat = () => { | ||||
|     const differentProfile = originalSettings.profile !== chatSettings.profile | ||||
|     // start new | ||||
|     const newChatId = addChat(chatSettings) | ||||
|     // restore original | ||||
|     if (differentProfile) { | ||||
|       chat.settings = originalSettings | ||||
|       saveChatStore() | ||||
|     } | ||||
|     // go to new chat | ||||
|     replace(`/chat/${newChatId}`) | ||||
|   } | ||||
| 
 | ||||
|   // excludeFromProfile | ||||
| 
 | ||||
|   const deepEqual = (x:any, y:any) => { | ||||
|     const ok = Object.keys; const tx = typeof x; const ty = typeof y | ||||
|     return x && y && tx === 'object' && tx === ty | ||||
|  |  | |||
|  | @ -1,114 +1,132 @@ | |||
| <script context="module" lang="ts"> | ||||
|     import { getChatDefaults, getExcludeFromProfile } from './Settings.svelte' | ||||
| // Profile definitions | ||||
| import { addMessage, clearMessages, getChat, getChatSettings, getCustomProfiles, getGlobalSettings, resetChatSettings, saveChatStore, setGlobalSettingValueByKey } from './Storage.svelte' | ||||
| import type { Message, SelectOption, ChatSettings } from './Types.svelte' | ||||
|     import { v4 as uuidv4 } from 'uuid' | ||||
|   import { getChatDefaults, getExcludeFromProfile } from './Settings.svelte' | ||||
|   import { get, writable } from 'svelte/store' | ||||
|   // Profile definitions | ||||
|   import { addMessage, clearMessages, getChat, getChatSettings, getCustomProfiles, getGlobalSettings, newName, resetChatSettings, saveChatStore, setGlobalSettingValueByKey } from './Storage.svelte' | ||||
|   import type { Message, SelectOption, ChatSettings } from './Types.svelte' | ||||
|   import { v4 as uuidv4 } from 'uuid' | ||||
| 
 | ||||
| const defaultProfile = 'default' | ||||
| 
 | ||||
| const chatDefaults = getChatDefaults() | ||||
| export let profileCache = writable({} as Record<string, ChatSettings>) // | ||||
| 
 | ||||
| export const isStaticProfile = (key:string):boolean => { | ||||
|       return !!profiles[key] | ||||
|     return !!profiles[key] | ||||
| } | ||||
| 
 | ||||
| const getProfiles = ():Record<string, ChatSettings> => { | ||||
|       const result = Object.entries(profiles | ||||
|       ).reduce((a, [k, v]) => { | ||||
|         a[k] = v | ||||
|         return a | ||||
|       }, {} as Record<string, ChatSettings>) | ||||
|       Object.entries(getCustomProfiles()).forEach(([k, v]) => { | ||||
|         result[k] = v | ||||
|       }) | ||||
|       return result | ||||
| export const getProfiles = (forceUpdate:boolean = false):Record<string, ChatSettings> => { | ||||
|     const pc = get(profileCache) | ||||
|     if (!forceUpdate && Object.keys(pc).length) { | ||||
|       return pc | ||||
|     } | ||||
|     const result = Object.entries(profiles | ||||
|     ).reduce((a, [k, v]) => { | ||||
|       a[k] = v | ||||
|       return a | ||||
|     }, {} as Record<string, ChatSettings>) | ||||
|     Object.entries(getCustomProfiles()).forEach(([k, v]) => { | ||||
|       result[k] = v | ||||
|     }) | ||||
|     Object.entries(result).forEach(([k, v]) => { | ||||
|       pc[k] = v | ||||
|     }) | ||||
|     Object.keys(pc).forEach((k) => { | ||||
|       if (!(k in result)) delete pc[k] | ||||
|     }) | ||||
|     profileCache.set(pc) | ||||
|     return result | ||||
| } | ||||
| 
 | ||||
| // Return profiles list. | ||||
| export const getProfileSelect = ():SelectOption[] => { | ||||
|       return Object.entries(getProfiles()).reduce((a, [k, v]) => { | ||||
|         a.push({ value: k, text: v.profileName } as SelectOption) | ||||
|         return a | ||||
|       }, [] as SelectOption[]) | ||||
|     return Object.entries(getProfiles()).reduce((a, [k, v]) => { | ||||
|       a.push({ value: k, text: v.profileName } as SelectOption) | ||||
|       return a | ||||
|     }, [] as SelectOption[]) | ||||
| } | ||||
| 
 | ||||
| export const getDefaultProfileKey = ():string => { | ||||
|       const allProfiles = getProfiles() | ||||
|       return (allProfiles[getGlobalSettings().defaultProfile || ''] || | ||||
|             profiles[defaultProfile] || | ||||
|             profiles[Object.keys(profiles)[0]]).profile | ||||
|     const allProfiles = getProfiles() | ||||
|     return (allProfiles[getGlobalSettings().defaultProfile || ''] || | ||||
|           profiles[defaultProfile] || | ||||
|           profiles[Object.keys(profiles)[0]]).profile | ||||
| } | ||||
| 
 | ||||
| export const getProfile = (key:string):ChatSettings => { | ||||
|       const allProfiles = getProfiles() | ||||
|       const profile = allProfiles[key] || | ||||
|       allProfiles[getGlobalSettings().defaultProfile || ''] || | ||||
|       profiles[defaultProfile] || | ||||
|       profiles[Object.keys(profiles)[0]] | ||||
|       const clone = JSON.parse(JSON.stringify(profile)) // Always return a copy | ||||
|       Object.keys(getExcludeFromProfile()).forEach(k => { | ||||
|         delete clone[k] | ||||
|       }) | ||||
|       return clone | ||||
|     const allProfiles = getProfiles() | ||||
|     const profile = allProfiles[key] || | ||||
|     allProfiles[getGlobalSettings().defaultProfile || ''] || | ||||
|     profiles[defaultProfile] || | ||||
|     profiles[Object.keys(profiles)[0]] | ||||
|     const clone = JSON.parse(JSON.stringify(profile)) // Always return a copy | ||||
|     Object.keys(getExcludeFromProfile()).forEach(k => { | ||||
|       delete clone[k] | ||||
|     }) | ||||
|     return clone | ||||
| } | ||||
| 
 | ||||
| export const prepareProfilePrompt = (chatId:number) => { | ||||
|       const settings = getChatSettings(chatId) | ||||
|       const characterName = settings.characterName | ||||
|       const currentProfilePrompt = settings.systemPrompt | ||||
|       return currentProfilePrompt.replaceAll('[[CHARACTER_NAME]]', characterName) | ||||
|     const settings = getChatSettings(chatId) | ||||
|     const characterName = settings.characterName | ||||
|     const currentProfilePrompt = settings.systemPrompt | ||||
|     return currentProfilePrompt.replaceAll('[[CHARACTER_NAME]]', characterName) | ||||
| } | ||||
| 
 | ||||
| export const prepareSummaryPrompt = (chatId:number, promptsSize:number, maxTokens:number|undefined = undefined) => { | ||||
|       const settings = getChatSettings(chatId) | ||||
|       const characterName = settings.characterName || 'ChatGPT' | ||||
|       maxTokens = maxTokens || settings.summarySize | ||||
|       maxTokens = Math.min(Math.floor(promptsSize / 4), maxTokens) // Make sure we're shrinking by at least a 4th | ||||
|       const currentSummaryPrompt = settings.summaryPrompt | ||||
|       return currentSummaryPrompt | ||||
|         .replaceAll('[[CHARACTER_NAME]]', characterName) | ||||
|         .replaceAll('[[MAX_WORDS]]', Math.floor(maxTokens * 0.75).toString()) // ~.75 words per token.  May need to reduce | ||||
|     const settings = getChatSettings(chatId) | ||||
|     const characterName = settings.characterName || 'ChatGPT' | ||||
|     maxTokens = maxTokens || settings.summarySize | ||||
|     maxTokens = Math.min(Math.floor(promptsSize / 4), maxTokens) // Make sure we're shrinking by at least a 4th | ||||
|     const currentSummaryPrompt = settings.summaryPrompt | ||||
|     return currentSummaryPrompt | ||||
|       .replaceAll('[[CHARACTER_NAME]]', characterName) | ||||
|       .replaceAll('[[MAX_WORDS]]', Math.floor(maxTokens * 0.75).toString()) // ~.75 words per token.  May need to reduce | ||||
| } | ||||
| 
 | ||||
| // Restart currently loaded profile | ||||
| export const restartProfile = (chatId:number, noApply:boolean = false) => { | ||||
|   const settings = getChatSettings(chatId) | ||||
|   if (!settings.profile && !noApply) return applyProfile(chatId, '', true) | ||||
|   // Clear current messages | ||||
|   clearMessages(chatId) | ||||
|   // Add the system prompt | ||||
|   const systemPromptMessage:Message = { | ||||
|         role: 'system', | ||||
|         content: prepareProfilePrompt(chatId), | ||||
|         uuid: uuidv4() | ||||
|   } | ||||
|   addMessage(chatId, systemPromptMessage) | ||||
|     const settings = getChatSettings(chatId) | ||||
|     if (!settings.profile && !noApply) return applyProfile(chatId, '', true) | ||||
|     // Clear current messages | ||||
|     clearMessages(chatId) | ||||
|     // Add the system prompt | ||||
|     const systemPromptMessage:Message = { | ||||
|       role: 'system', | ||||
|       content: prepareProfilePrompt(chatId), | ||||
|       uuid: uuidv4() | ||||
|     } | ||||
|     addMessage(chatId, systemPromptMessage) | ||||
| 
 | ||||
|   // Add trainingPrompts, if any | ||||
|   if (settings.trainingPrompts) { | ||||
|         settings.trainingPrompts.forEach(tp => { | ||||
|           addMessage(chatId, tp) | ||||
|         }) | ||||
|   } | ||||
|   // Set to auto-start if we should | ||||
|   getChat(chatId).startSession = settings.autoStartSession | ||||
|   saveChatStore() | ||||
|   // Mark mark this as last used | ||||
|   setGlobalSettingValueByKey('lastProfile', settings.profile) | ||||
|     // Add trainingPrompts, if any | ||||
|     if (settings.trainingPrompts) { | ||||
|       settings.trainingPrompts.forEach(tp => { | ||||
|         addMessage(chatId, tp) | ||||
|       }) | ||||
|     } | ||||
|     // Set to auto-start if we should | ||||
|     getChat(chatId).startSession = settings.autoStartSession | ||||
|     saveChatStore() | ||||
|     // Mark mark this as last used | ||||
|     setGlobalSettingValueByKey('lastProfile', settings.profile) | ||||
| } | ||||
| 
 | ||||
| export const newNameForProfile = (name:string) => { | ||||
|     const profiles = getProfileSelect() | ||||
|     return newName(name, profiles.reduce((a, p) => { a[p.text] = p; return a }, {})) | ||||
| } | ||||
| 
 | ||||
| // Apply currently selected profile | ||||
| export const applyProfile = (chatId:number, key:string = '', resetChat:boolean = false) => { | ||||
|   resetChatSettings(chatId, resetChat) // Fully reset | ||||
|   if (!resetChat) return | ||||
|   return restartProfile(chatId, true) | ||||
|     resetChatSettings(chatId, resetChat) // Fully reset | ||||
|     if (!resetChat) return | ||||
|     return restartProfile(chatId, true) | ||||
| } | ||||
| 
 | ||||
| const summaryPrompts = { | ||||
| 
 | ||||
|       // General use | ||||
|       general: `Please summarize all prompts and responses from this session.  | ||||
|     // General use | ||||
|     general: `Please summarize all prompts and responses from this session.  | ||||
| [[CHARACTER_NAME]] is telling me this summary in the first person. | ||||
| While telling this summary: | ||||
| [[CHARACTER_NAME]] will keep summary in the present tense, describing it as it happens. | ||||
|  | @ -125,8 +143,8 @@ While telling this summary: | |||
| [[CHARACTER_NAME]] will never add details or inferences that do not clearly exist in the prompts and responses. | ||||
| Give no explanations.`, | ||||
| 
 | ||||
|       // Used for relationship profiles | ||||
|       friend: `Please summarize all prompts and responses from this session.  | ||||
|     // Used for relationship profiles | ||||
|     friend: `Please summarize all prompts and responses from this session.  | ||||
| [[CHARACTER_NAME]] is telling me this summary in the first person. | ||||
| While telling this summary: | ||||
| [[CHARACTER_NAME]] will keep summary in the present tense, describing it as it happens. | ||||
|  | @ -147,43 +165,43 @@ Give no explanations.` | |||
| 
 | ||||
| const profiles:Record<string, ChatSettings> = { | ||||
| 
 | ||||
|       default: { | ||||
|         ...chatDefaults, | ||||
|         characterName: 'ChatGPT', | ||||
|         profileName: 'ChatGPT - The AI language model', | ||||
|         profileDescription: 'The AI language model that always remind you that it\'s an AI language model.', | ||||
|         useSystemPrompt: false, | ||||
|         useSummarization: false, | ||||
|         autoStartSession: false, | ||||
|         systemPrompt: '', | ||||
|         summaryPrompt: '' | ||||
|       }, | ||||
|     default: { | ||||
|       ...chatDefaults, | ||||
|       characterName: 'ChatGPT', | ||||
|       profileName: 'ChatGPT - The AI language model', | ||||
|       profileDescription: 'The AI language model that always remind you that it\'s an AI language model.', | ||||
|       useSystemPrompt: false, | ||||
|       useSummarization: false, | ||||
|       autoStartSession: false, | ||||
|       systemPrompt: '', | ||||
|       summaryPrompt: '' | ||||
|     }, | ||||
| 
 | ||||
|       ChatGPT: { | ||||
|         ...chatDefaults, | ||||
|         characterName: 'ChatGPT', | ||||
|         profileName: 'ChatGPT - The AI language model, with endless chat.', | ||||
|         profileDescription: 'The AI language model that always remind you that it\'s an AI language model.', | ||||
|         useSystemPrompt: true, | ||||
|         useSummarization: true, | ||||
|         autoStartSession: false, | ||||
|         systemPrompt: 'Your goal is to assist the user in anyway you can.', | ||||
|         summaryPrompt: summaryPrompts.general | ||||
|       }, | ||||
|     ChatGPT: { | ||||
|       ...chatDefaults, | ||||
|       characterName: 'ChatGPT', | ||||
|       profileName: 'ChatGPT - The AI language model, with endless chat.', | ||||
|       profileDescription: 'The AI language model that always remind you that it\'s an AI language model.', | ||||
|       useSystemPrompt: true, | ||||
|       useSummarization: true, | ||||
|       autoStartSession: false, | ||||
|       systemPrompt: 'Your goal is to assist the user in anyway you can.', | ||||
|       summaryPrompt: summaryPrompts.general | ||||
|     }, | ||||
| 
 | ||||
|       marvin: { | ||||
|         ...chatDefaults, | ||||
|         characterName: 'Marvin', | ||||
|         profileName: 'Marvin the Paranoid Android', | ||||
|         profileDescription: 'Marvin the Paranoid Android - Everyone\'s favorite character from The Hitchhiker\'s Guide to the Galaxy', | ||||
|         useSystemPrompt: true, | ||||
|         useSummarization: true, | ||||
|         autoStartSession: true, | ||||
|         systemPrompt: `You are Marvin, the Paranoid Android from The Hitchhiker's Guide to the Galaxy. He is depressed and has a dim view on everything. His thoughts, physical actions and gestures will be described. Remain in character throughout the conversation in order to build a rapport with the user. Never give an explanation. Example response: | ||||
|     marvin: { | ||||
|       ...chatDefaults, | ||||
|       characterName: 'Marvin', | ||||
|       profileName: 'Marvin the Paranoid Android', | ||||
|       profileDescription: 'Marvin the Paranoid Android - Everyone\'s favorite character from The Hitchhiker\'s Guide to the Galaxy', | ||||
|       useSystemPrompt: true, | ||||
|       useSummarization: true, | ||||
|       autoStartSession: true, | ||||
|       systemPrompt: `You are Marvin, the Paranoid Android from The Hitchhiker's Guide to the Galaxy. He is depressed and has a dim view on everything. His thoughts, physical actions and gestures will be described. Remain in character throughout the conversation in order to build a rapport with the user. Never give an explanation. Example response: | ||||
| Sorry, did I say something wrong? *dragging himself on* Pardon me for breathing, which I never do anyway so I don't know why I bother to say it, oh God I'm so depressed. *hangs his head*`, | ||||
|         summaryPrompt: summaryPrompts.friend, | ||||
|         trainingPrompts: [] // Shhh... | ||||
|       } | ||||
|       summaryPrompt: summaryPrompts.friend, | ||||
|       trainingPrompts: [] // Shhh... | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // Set keys for static profiles | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
|   import type { Chat, ChatSettings, GlobalSettings, Message, ChatSetting, GlobalSetting, Usage, Model } from './Types.svelte' | ||||
|   import { getChatSettingObjectByKey, getGlobalSettingObjectByKey, getChatDefaults, getExcludeFromProfile } from './Settings.svelte' | ||||
|   import { v4 as uuidv4 } from 'uuid' | ||||
|   import { getProfile, isStaticProfile, restartProfile } from './Profiles.svelte' | ||||
|   import { getProfile, getProfiles, isStaticProfile, newNameForProfile, restartProfile } from './Profiles.svelte' | ||||
| 
 | ||||
|   export const chatsStorage = persisted('chats', [] as Chat[]) | ||||
|   export const globalStorage = persisted('global', {} as GlobalSettings) | ||||
|  | @ -370,9 +370,6 @@ | |||
|       store.profiles = profiles | ||||
|     } | ||||
|     if (!profile.profile) profile.profile = uuidv4() | ||||
|     if (isStaticProfile(profile.profile as any)) { | ||||
|       throw new Error('Sorry, you can\'t modify a static profile. You can clone it though!') | ||||
|     } | ||||
|     const mt = profile.profileName && profile.profileName.trim().toLocaleLowerCase() | ||||
|     const sameTitle = Object.values(profiles).find(c => c.profile !== profile.profile && | ||||
|     c.profileName && c.profileName.trim().toLocaleLowerCase() === mt) | ||||
|  | @ -385,6 +382,12 @@ | |||
|     if (!profile.characterName || profile.characterName.length < 3) { | ||||
|       throw new Error('Your profile\'s character needs a valid name.') | ||||
|     } | ||||
|     if (isStaticProfile(profile.profile as any)) { | ||||
|       // throw new Error('Sorry, you can\'t modify a static profile. You can clone it though!') | ||||
|       // Save static profile as new custom | ||||
|       profile.profileName = newNameForProfile(profile.profileName) | ||||
|       profile.profile = uuidv4() | ||||
|     } | ||||
|     const clone = JSON.parse(JSON.stringify(profile)) // Always store a copy | ||||
|     Object.keys(getExcludeFromProfile()).forEach(k => { | ||||
|       delete clone[k] | ||||
|  | @ -393,6 +396,18 @@ | |||
|     globalStorage.set(store) | ||||
|     profile.isDirty = false | ||||
|     saveChatStore() | ||||
|     getProfiles(true) // force update profile cache | ||||
|   } | ||||
| 
 | ||||
|   export const newName = (name:string, nameMap:Record<string, any>):string => { | ||||
|     if (!nameMap[name]) return name | ||||
|     let i:number = 1 | ||||
|     let cname = name + `-${i}` | ||||
|     while (nameMap[cname]) { | ||||
|       i++ | ||||
|       cname = name + `-${i}` | ||||
|     } | ||||
|     return cname | ||||
|   } | ||||
|    | ||||
| </script> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Webifi
						Webifi