From 2d141a48a427fe25665852306401d70072a4ef33 Mon Sep 17 00:00:00 2001 From: hanishkvc Date: Sat, 22 Nov 2025 14:02:14 +0530 Subject: [PATCH] SimpleChatTCRV:DivStreams: Enhanced Object using JSDoc++ Make DivStream hold a Role element and Data element and inturn have the live got data go into the data element. Set some of the relavent classes wrt these, so that it is themed matching other chat blocks, to any extent. Add a clear helper function to cleanup as and when needed. NOTE: Remember to use this to get hold of the DivStream instance being worked on. NOTE: Dont forget that setting a parent wrt a HTMLElement wont automatically add it to the corresponding DOM with a parent child relation. The new html element will just remain in memory ignored by everyone else. --- tools/server/public_simplechat/simplechat.js | 61 +++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/tools/server/public_simplechat/simplechat.js b/tools/server/public_simplechat/simplechat.js index 0b3e921167..6d3df25b6d 100644 --- a/tools/server/public_simplechat/simplechat.js +++ b/tools/server/public_simplechat/simplechat.js @@ -475,7 +475,17 @@ function usage_note(sRecentUserMsgCnt) { } -/** @typedef {{ chatPropsStream: boolean, toolsEnabled: boolean}} SCHandshakeProps*/ +/** + * @typedef {HTMLDivElement & { + * divStreamRole: HTMLDivElement, + * divStreamData: HTMLDivElement, + * divstream_clear: () => void, + * }} ELDivStream + */ + +/** @typedef {Object} ELDivStreams */ + +/** @typedef {{ chatPropsStream: boolean, toolsEnabled: boolean}} SCHandshakeProps */ /** @typedef {ChatMessageEx[]} ChatMessages */ @@ -874,13 +884,14 @@ class SimpleChat { * Handle the multipart response from server/ai-model * @param {Response} resp * @param {string} apiEP - * @param {Object} elDivStreams + * @param {ELDivStreams} elDivStreams */ async handle_response_multipart(resp, apiEP, elDivStreams) { if (!resp.body) { throw Error("ERRR:SimpleChat:SC:HandleResponseMultiPart:No body..."); } - let elP = elDivStreams[this.chatId]; + let elDiv = elDivStreams[this.chatId]; + elDiv.divStreamRole.innerText = `Ai:${this.chatId.slice(0,6)}` try { let tdUtf8 = new TextDecoder("utf-8"); let rr = resp.body.getReader(); @@ -912,14 +923,14 @@ class SimpleChat { console.debug("DBUG:SC:PART:Json:", curJson); this.latestResponse.update_stream(curJson, apiEP); } - elP.innerText = this.latestResponse.content_equiv() - elP.scrollIntoView(false); + elDiv.divStreamData.innerText = this.latestResponse.content_equiv() + elDiv.scrollIntoView(false); if (done) { break; } } } finally { - elP.replaceChildren() + elDiv.divstream_clear() } console.debug("DBUG:SC:PART:Full:", this.latestResponse.content_equiv()); return ChatMessageEx.newFrom(this.latestResponse); @@ -943,7 +954,7 @@ class SimpleChat { * Also take care of the optional garbage trimming. * @param {Response} resp * @param {string} apiEP - * @param {Object} elDivStreams - used to place ai server chat response as it is being generated/recieved in streaming mode + * @param {ELDivStreams} elDivStreams - used to place ai server chat response as it is being generated/recieved in streaming mode */ async handle_response(resp, apiEP, elDivStreams) { let theResp = null; @@ -951,7 +962,7 @@ class SimpleChat { if (this.handshakeProps.chatPropsStream) { theResp = await this.handle_response_multipart(resp, apiEP, elDivStreams); this.latestResponse.clear(); - elDivStreams[this.chatId].replaceChildren() + elDivStreams[this.chatId].divstream_clear(); } else { theResp = await this.handle_response_oneshot(resp, apiEP); } @@ -960,7 +971,7 @@ class SimpleChat { theResp.ns.role = Roles.Assistant; this.add(theResp); this.latestResponse.clear(); - elDivStreams[this.chatId].replaceChildren() + elDivStreams[this.chatId].divstream_clear() throw error; } if (this.me.chatProps.bTrimGarbage) { @@ -980,7 +991,7 @@ class SimpleChat { * @param {string} baseURL * @param {string} apiEP * @param {SCHandshakeProps} hsProps - * @param {Object} elDivStreams - used to show chat response as it is being generated/recieved in streaming mode + * @param {ELDivStreams} elDivStreams - used to show chat response as it is being generated/recieved in streaming mode */ async handle_chat_hs(baseURL, apiEP, hsProps, elDivStreams) { class ChatHSError extends Error { @@ -1139,7 +1150,7 @@ class MultiChatUI { this.elBtnUser.parentElement?.appendChild(this.elInFileX.elB) // other ui elements - /** @type {Object} */ + /** @type {ELDivStreams} */ this.elDivStreams = {} this.validate_element(this.elInSystem, "system-in"); @@ -1437,7 +1448,7 @@ class MultiChatUI { if (bClear) { this.elDivChat.replaceChildren(); this.ui_userinput_reset() - this.elDivStreams[chatId]?.replaceChildren() + this.elDivStreams[chatId]?.divstream_clear() } this.ui_reset_toolcall_as_needed(new ChatMessageEx()); this.elLastChatMessage = null @@ -1680,9 +1691,29 @@ class MultiChatUI { */ new_chat_session(chatId, bSwitchSession=false) { this.simpleChats[chatId] = new SimpleChat(chatId, this.me); - this.elDivStreams[chatId] = document.createElement('div') - this.elDivStreams[chatId].id = `DivStream-${chatId}` - this.elDivStreams[chatId].classList.add("chat-message-content-live") + /** @type {ELDivStream} */ + // @ts-ignore + let elDiv = Object.assign(document.createElement('div'), { + id: `DivStream-${chatId}`, + className: 'chat-message', + }); + let elDivRole = Object.assign(document.createElement('div'), { + id: 'divStreamRole', + className: 'chat-message-role', + }); + elDiv.appendChild(elDivRole) + elDiv.divStreamRole = elDivRole + let elDivData = Object.assign(document.createElement('div'), { + id: 'divStreamData', + className: 'chat-message-content-live', + }); + elDiv.appendChild(elDivData) + elDiv.divStreamData = elDivData + elDiv.divstream_clear = function() { + this.divStreamRole.replaceChildren() + this.divStreamData.replaceChildren() + } + this.elDivStreams[chatId] = elDiv; if (bSwitchSession) { this.handle_session_switch(chatId); }