From 570131943fef5461d508ad44049fc8853bb84373 Mon Sep 17 00:00:00 2001 From: hanishkvc Date: Tue, 11 Nov 2025 22:17:07 +0530 Subject: [PATCH] SimpleChatTCRV:iDB:Add Put/Get; SimpleChat Save/Load using iDB Added put and get helpers wrt indexedDB. Updated save and load related logics in SimpleChatTCRV. --- tools/server/public_simplechat/idb.mjs | 60 ++++++++++++++++ tools/server/public_simplechat/readme.md | 5 ++ tools/server/public_simplechat/simplechat.js | 75 ++++++++++++++------ 3 files changed, 120 insertions(+), 20 deletions(-) diff --git a/tools/server/public_simplechat/idb.mjs b/tools/server/public_simplechat/idb.mjs index 673c1c8541..3b065996d3 100644 --- a/tools/server/public_simplechat/idb.mjs +++ b/tools/server/public_simplechat/idb.mjs @@ -32,6 +32,7 @@ export function db_open(dbName, storeName, callerTag="") { }); } + /** * Get hold of a transaction wrt a specified store in the db * @param {IDBDatabase} db @@ -43,3 +44,62 @@ export function db_trans_store(db, storeName, opMode) { let dbOS = dbTrans.objectStore(storeName); return dbOS } + + +/** + * Put a given key-value pair into a store in a db. + * Return success or failure through callback. + * + * @param {string} dbName + * @param {string} storeName + * @param {IDBValidKey} key + * @param {any} value + * @param {string | undefined} callerTag + * @param {(status: boolean, related: IDBValidKey | DOMException | null) => void} cb + */ +export function db_put(dbName, storeName, key, value, callerTag, cb) { + let tag = `iDB:Put:${callerTag}`; + db_open(dbName, storeName, tag).then((/** @type {IDBDatabase} */db)=>{ + let reqPut = db_trans_store(db, storeName, 'readwrite').put(value, key) + reqPut.onerror = (evPut) => { + console.info(`ERRR:${tag}:OnError:transact failed:${reqPut.error}`) + cb(false, reqPut.error) + } + reqPut.onsuccess = (evPut) => { + console.info(`DBUG:${tag}:transact success`) + cb(true, reqPut.result) + } + }).catch((errReason)=>{ + console.info(`ERRR:${tag}:Caught:transact failed:${errReason}`) + cb(false, errReason) + }) +} + + +/** + * Return value of specified key from a store in a db, + * through the provided callback. + * + * @param {string} dbName + * @param {string} storeName + * @param {IDBValidKey} key + * @param {string | undefined} callerTag + * @param {(status: boolean, related: IDBValidKey | DOMException | null) => void} cb + */ +export function db_get(dbName, storeName, key, callerTag, cb) { + let tag = `iDB:Get:${callerTag}`; + db_open(dbName, storeName, tag).then((/** @type {IDBDatabase} */db)=>{ + let reqGet = db_trans_store(db, storeName, 'readonly').get(key); + reqGet.onsuccess = (evGet) => { + console.info(`DBUG:${tag}:transact success`) + cb(true, reqGet.result) + } + reqGet.onerror = (evGet) => { + console.info(`ERRR:${tag}:OnError:transact failed:${reqGet.error}`) + cb(false, reqGet.error) + } + }).catch((errReason)=>{ + console.info(`ERRR:${tag}:Caught:transact failed:${errReason}`) + cb(false, errReason) + }) +} diff --git a/tools/server/public_simplechat/readme.md b/tools/server/public_simplechat/readme.md index f1fc84bec0..55cad4cd3b 100644 --- a/tools/server/public_simplechat/readme.md +++ b/tools/server/public_simplechat/readme.md @@ -734,6 +734,11 @@ sliding window based drop off or even before they kick in, this can help in many * SimpleChat class now allows extra fields to be specified while adding, in a generic way using a object/literal object or equivalent. +* UI Cleanup - msgs spaced out, toolcall edit hr not always, scroll ui only when required, + hide settings/info till user requests, heading gradient + +* iDB module - add open, transact, put and get. Use for chat session save and load + #### ToDo diff --git a/tools/server/public_simplechat/simplechat.js b/tools/server/public_simplechat/simplechat.js index 201452a64f..78e15490a3 100644 --- a/tools/server/public_simplechat/simplechat.js +++ b/tools/server/public_simplechat/simplechat.js @@ -6,6 +6,11 @@ import * as du from "./datautils.mjs"; import * as ui from "./ui.mjs" import * as mTools from "./tools.mjs" +import * as mIdb from "./idb.mjs" + + +const DB_NAME = "SimpleChatTCRV" +const DB_STORE = "Sessions" const ROLES_TEMP_ENDSWITH = ".TEMP" @@ -468,29 +473,48 @@ class SimpleChat { } /** - * Save into localStorage + * Save into indexedDB */ save() { + let tag = `SimpleChat:Save:${this.chatId}`; /** @type {SimpleChatODS} */ let ods = {iLastSys: this.iLastSys, xchat: this.xchat}; - localStorage.setItem(this.ods_key(), JSON.stringify(ods)); + mIdb.db_put(DB_NAME, DB_STORE, this.ods_key(), JSON.stringify(ods), tag, (status, related)=>{ + console.log(`DBUG:${tag}:${status}:${related}`) + if (!status) { + throw new Error(`ERRR:${tag}:${related}`) + } + }) } /** - * Load from localStorage + * Load from indexedDB, get status through callback. + * @param {((loadStatus: boolean, dbStatus: boolean, related: IDBValidKey | DOMException | null) => void)} cb */ - load() { - let sods = localStorage.getItem(this.ods_key()); - if (sods == null) { - return; - } - /** @type {SimpleChatODS} */ - let ods = JSON.parse(sods); - this.iLastSys = ods.iLastSys; - this.xchat = []; - for (const cur of ods.xchat) { - this.xchat.push(new ChatMessageEx(new NSChatMessage(cur.ns.role, cur.ns.content, cur.ns.reasoning_content, cur.ns.tool_calls, cur.ns.tool_call_id, cur.ns.name, cur.ns.image_url), cur.trimmedContent)) - } + load(cb) { + let tag = `SimpleChat:Load:${this.chatId}`; + mIdb.db_get(DB_NAME, DB_STORE, this.ods_key(), tag, (status, related)=>{ + if (!status) { + cb(false, status, `ERRR:${tag}:Db failure:${related}`); + return + } + if (!related) { + cb(false, status, `ERRR:${tag}:No data?`); + return + } + if (typeof(related) == "string") { + /** @type {SimpleChatODS} */ + let ods = JSON.parse(related); + this.iLastSys = ods.iLastSys; + this.xchat = []; + for (const cur of ods.xchat) { + this.xchat.push(new ChatMessageEx(new NSChatMessage(cur.ns.role, cur.ns.content, cur.ns.reasoning_content, cur.ns.tool_calls, cur.ns.tool_call_id, cur.ns.name, cur.ns.image_url), cur.trimmedContent)) + } + cb(true, status, related) + } else { + cb(false, status, `ERRR:${tag}:DOMException?:${related}`); + } + }) } /** @@ -587,6 +611,10 @@ class SimpleChat { * * NOTE: A new copy is created and added into xchat. * Also update iLastSys system prompt index tracker + * + * ALERT: Also triggers a save, which occurs assynchronously in the background, as of now, + * with no handle returned wrt same. + * * @param {ChatMessageEx} chatMsg * @param {Object|undefined} extra - optional additional fieldName=Value pairs to be added, if any */ @@ -1652,11 +1680,18 @@ export class Me { div.innerHTML += `

Restore

Load previously saved chat session, if available

`; let btn = ui.el_create_button(chat.ods_key(), (ev)=>{ - console.log("DBUG:SimpleChat:SC:Load", chat); - chat.load(); - queueMicrotask(()=>{ - this.multiChat.chat_show(chat.chatId, true, true); - this.multiChat.elInSystem.value = chat.get_system_latest().ns.getContent(); + let tag = `Me:Load:${chat.chatId}`; + console.log(`DBUG:${tag}`, chat); + chat.load((loadStatus, dbStatus, related)=>{ + if (!loadStatus || !dbStatus) { + console.log(`WARN:${tag}:DidntLoad:${loadStatus}:${dbStatus}:${related}`); + return; + } + console.log(`INFO:${tag}:Loaded:${loadStatus}:${dbStatus}`); + queueMicrotask(()=>{ + this.multiChat.chat_show(chat.chatId, true, true); + this.multiChat.elInSystem.value = chat.get_system_latest().ns.getContent(); + }); }); }); div.appendChild(btn);