KBaba7/llama.cpp
0
1// coversations is stored in localStorage2// format: { [convId]: { id: string, lastModified: number, messages: [...] } }3 4import { CONFIG_DEFAULT } from '../Config';5import { Conversation, Message } from './types';6 7const event = new EventTarget();8 9type CallbackConversationChanged = (convId: string) => void;10let onConversationChangedHandlers: [11 CallbackConversationChanged,12 EventListener,13][] = [];14const dispatchConversationChange = (convId: string) => {15 event.dispatchEvent(16 new CustomEvent('conversationChange', { detail: { convId } })17 );18};19 20// convId is a string prefixed with 'conv-'21const StorageUtils = {22 /**23 * manage conversations24 */25 getAllConversations(): Conversation[] {26 const res = [];27 for (const key in localStorage) {28 if (key.startsWith('conv-')) {29 res.push(JSON.parse(localStorage.getItem(key) ?? '{}'));30 }31 }32 res.sort((a, b) => b.lastModified - a.lastModified);33 return res;34 },35 /**36 * can return null if convId does not exist37 */38 getOneConversation(convId: string): Conversation | null {39 return JSON.parse(localStorage.getItem(convId) || 'null');40 },41 /**42 * if convId does not exist, create one43 */44 appendMsg(convId: string, msg: Message): void {45 if (msg.content === null) return;46 const conv = StorageUtils.getOneConversation(convId) || {47 id: convId,48 lastModified: Date.now(),49 messages: [],50 };51 conv.messages.push(msg);52 conv.lastModified = Date.now();53 localStorage.setItem(convId, JSON.stringify(conv));54 dispatchConversationChange(convId);55 },56 /**57 * Get new conversation id58 */59 getNewConvId(): string {60 return `conv-${Date.now()}`;61 },62 /**63 * remove conversation by id64 */65 remove(convId: string): void {66 localStorage.removeItem(convId);67 dispatchConversationChange(convId);68 },69 /**70 * remove all conversations71 */72 filterAndKeepMsgs(73 convId: string,74 predicate: (msg: Message) => boolean75 ): void {76 const conv = StorageUtils.getOneConversation(convId);77 if (!conv) return;78 conv.messages = conv.messages.filter(predicate);79 conv.lastModified = Date.now();80 localStorage.setItem(convId, JSON.stringify(conv));81 dispatchConversationChange(convId);82 },83 /**84 * remove last message from conversation85 */86 popMsg(convId: string): Message | undefined {87 const conv = StorageUtils.getOneConversation(convId);88 if (!conv) return;89 const msg = conv.messages.pop();90 conv.lastModified = Date.now();91 if (conv.messages.length === 0) {92 StorageUtils.remove(convId);93 } else {94 localStorage.setItem(convId, JSON.stringify(conv));95 }96 dispatchConversationChange(convId);97 return msg;98 },99 100 // event listeners101 onConversationChanged(callback: CallbackConversationChanged) {102 const fn = (e: Event) => callback((e as CustomEvent).detail.convId);103 onConversationChangedHandlers.push([callback, fn]);104 event.addEventListener('conversationChange', fn);105 },106 offConversationChanged(callback: CallbackConversationChanged) {107 const fn = onConversationChangedHandlers.find(([cb, _]) => cb === callback);108 if (fn) {109 event.removeEventListener('conversationChange', fn[1]);110 }111 onConversationChangedHandlers = [];112 },113 114 // manage config115 getConfig(): typeof CONFIG_DEFAULT {116 const savedVal = JSON.parse(localStorage.getItem('config') || '{}');117 // to prevent breaking changes in the future, we always provide default value for missing keys118 return {119 ...CONFIG_DEFAULT,120 ...savedVal,121 };122 },123 setConfig(config: typeof CONFIG_DEFAULT) {124 localStorage.setItem('config', JSON.stringify(config));125 },126 getTheme(): string {127 return localStorage.getItem('theme') || 'auto';128 },129 setTheme(theme: string) {130 if (theme === 'auto') {131 localStorage.removeItem('theme');132 } else {133 localStorage.setItem('theme', theme);134 }135 },136};137 138export default StorageUtils;139 