From 7d1760f1b828fc97c973fe164af94e5b9c234c61 Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 20 Apr 2025 22:43:10 +0200 Subject: [PATCH] Enhance plain text handling by integrating default preferences and adding HTML stripping functionality. see #350 --- api_webchat/controller.js | 13 +++++++++---- api_webchat/messagesArea.js | 12 +++++++++++- js/mzta-utils.js | 10 ++++++++++ mzta-background.js | 9 ++++++--- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/api_webchat/controller.js b/api_webchat/controller.js index ec23e569..06d3e79e 100644 --- a/api_webchat/controller.js +++ b/api_webchat/controller.js @@ -21,6 +21,7 @@ */ import { placeholdersUtils } from '../js/mzta-placeholders.js'; +import { prefs_default } from '../options/mzta-options-default.js'; // Get the LLM to be used const urlParams = new URLSearchParams(window.location.search); @@ -66,48 +67,52 @@ messageInput.setMessagesArea(messagesArea); switch (llm) { case "chatgpt_api": { - let prefs_api = await browser.storage.sync.get({chatgpt_api_key: '', chatgpt_model: '', chatgpt_developer_messages:'', do_debug: false}); + let prefs_api = await browser.storage.sync.get({chatgpt_api_key: prefs_default.chatgpt_api_key, chatgpt_model: prefs_default.chatgpt_model, chatgpt_developer_messages:prefs_default.chatgpt_developer_messages, do_debug: prefs_default.do_debug, composing_plain_text: prefs_default.composing_plain_text}); let i18nStrings = {}; i18nStrings["chatgpt_api_request_failed"] = browser.i18n.getMessage('chatgpt_api_request_failed'); i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted'); messageInput.setModel(prefs_api.chatgpt_model); messagesArea.setLLMName("ChatGPT"); + messagesArea.setPlainTextMode(prefs_api.composing_plain_text); worker.postMessage({ type: 'init', chatgpt_api_key: prefs_api.chatgpt_api_key, chatgpt_model: prefs_api.chatgpt_model, chatgpt_developer_messages: prefs_api.chatgpt_developer_messages, do_debug: prefs_api.do_debug, i18nStrings: i18nStrings}); messagesArea.appendUserMessage(browser.i18n.getMessage("chagpt_api_connecting") + " " +browser.i18n.getMessage("AndModel") + " \"" + prefs_api.chatgpt_model + "\"...", "info"); browser.runtime.sendMessage({command: "openai_api_ready_" + call_id, window_id: (await browser.windows.getCurrent()).id}); break; } case "google_gemini_api": { - let prefs_api = await browser.storage.sync.get({google_gemini_api_key: '', google_gemini_model: '', google_gemini_system_instruction: '', do_debug: false}); + let prefs_api = await browser.storage.sync.get({google_gemini_api_key: prefs_default.google_gemini_api_key, google_gemini_model: prefs_default.google_gemini_model, google_gemini_system_instruction: prefs_default.google_gemini_system_instruction, do_debug: prefs_default.do_debug, composing_plain_text: prefs_default.composing_plain_text}); let i18nStrings = {}; i18nStrings["google_gemini_api_request_failed"] = browser.i18n.getMessage('google_gemini_api_request_failed'); i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted'); messageInput.setModel(prefs_api.google_gemini_model); messagesArea.setLLMName("Google Gemini"); + messagesArea.setPlainTextMode(prefs_api.composing_plain_text); worker.postMessage({ type: 'init', google_gemini_api_key: prefs_api.google_gemini_api_key, google_gemini_model: prefs_api.google_gemini_model, google_gemini_system_instruction: prefs_api.google_gemini_system_instruction, do_debug: prefs_api.do_debug, i18nStrings: i18nStrings}); messagesArea.appendUserMessage(browser.i18n.getMessage("google_gemini_api_connecting") + " " +browser.i18n.getMessage("AndModel") + " \"" + prefs_api.google_gemini_model + "\"...", "info"); browser.runtime.sendMessage({command: "google_gemini_api_ready_" + call_id, window_id: (await browser.windows.getCurrent()).id}); break; } case "ollama_api": { - let prefs_api = await browser.storage.sync.get({ollama_host: '', ollama_model: '', do_debug: false}); + let prefs_api = await browser.storage.sync.get({ollama_host: prefs_default.ollama_host, ollama_model: prefs_default.ollama_model, do_debug: prefs_default.do_debug, composing_plain_text: prefs_default.composing_plain_text}); let i18nStrings = {}; i18nStrings["ollama_api_request_failed"] = browser.i18n.getMessage('ollama_api_request_failed'); i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted'); messageInput.setModel(prefs_api.ollama_model); messagesArea.setLLMName("Ollama Local"); + messagesArea.setPlainTextMode(prefs_api.composing_plain_text); worker.postMessage({ type: 'init', ollama_host: prefs_api.ollama_host, ollama_model: prefs_api.ollama_model, do_debug: prefs_api.do_debug, i18nStrings: i18nStrings}); browser.runtime.sendMessage({command: "ollama_api_ready_" + call_id, window_id: (await browser.windows.getCurrent()).id}); messagesArea.appendUserMessage(browser.i18n.getMessage("ollama_api_connecting") + " \"" + prefs_api.ollama_host + "\" " +browser.i18n.getMessage("AndModel") + " \"" + prefs_api.ollama_model + "\"...", "info"); break; } case "openai_comp_api": { - let prefs_api = await browser.storage.sync.get({openai_comp_host: '', openai_comp_model: '', openai_comp_api_key: '', openai_comp_use_v1: true, openai_comp_chat_name: '', do_debug: false}); + let prefs_api = await browser.storage.sync.get({openai_comp_host: prefs_default.openai_comp_host, openai_comp_model: prefs_default.openai_comp_model, openai_comp_api_key: prefs_default.openai_comp_api_key, openai_comp_use_v1: prefs_default.openai_comp_use_v1, openai_comp_chat_name: prefs_default.openai_comp_chat_name, do_debug: prefs_default.do_debug, composing_plain_text: prefs_default.composing_plain_text}); let i18nStrings = {}; i18nStrings["OpenAIComp_api_request_failed"] = browser.i18n.getMessage('OpenAIComp_api_request_failed'); i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted'); messageInput.setModel(prefs_api.openai_comp_model); messagesArea.setLLMName(prefs_api.openai_comp_chat_name); + messagesArea.setPlainTextMode(prefs_api.composing_plain_text); worker.postMessage({ type: 'init', openai_comp_host: prefs_api.openai_comp_host, openai_comp_model: prefs_api.openai_comp_model, openai_comp_api_key: prefs_api.openai_comp_api_key, openai_comp_use_v1: prefs_api.openai_comp_use_v1, do_debug: prefs_api.do_debug, i18nStrings: i18nStrings}); messagesArea.appendUserMessage(browser.i18n.getMessage("OpenAIComp_api_connecting") + " \"" + prefs_api.openai_comp_host + "\" " +browser.i18n.getMessage("AndModel") + " \"" + prefs_api.openai_comp_model + "\"...", "info"); browser.runtime.sendMessage({command: "openai_comp_api_ready_" + call_id, window_id: (await browser.windows.getCurrent()).id}); diff --git a/api_webchat/messagesArea.js b/api_webchat/messagesArea.js index 186c0ec2..240875cf 100644 --- a/api_webchat/messagesArea.js +++ b/api_webchat/messagesArea.js @@ -20,6 +20,8 @@ * The original code has been released under the Apache License, Version 2.0. */ +import { stripHtmlKeepLines } from '../js/mzta-utils.js'; + const messagesAreaTemplate = document.createElement('template'); const messagesAreaStyle = document.createElement('style'); @@ -118,6 +120,7 @@ class MessagesArea extends HTMLElement { fullTextHTML = ""; llmName = "LLM"; + plainTextMode = false; constructor() { super(); @@ -152,6 +155,10 @@ class MessagesArea extends HTMLElement { this.llmName = llmName; } + setPlainTextMode(plainTextMode) { + this.plainTextMode = plainTextMode; + } + handleTokensDone(promptData = null) { this.flushAccumulatingMessage(); this.addActionButtons(promptData); @@ -229,7 +236,10 @@ class MessagesArea extends HTMLElement { actionButtons.classList.add('action-buttons'); const actionButton = document.createElement('button'); actionButton.textContent = browser.i18n.getMessage("apiwebchat_use_this_answer"); - const fullTextHTMLAtAssignment = this.fullTextHTML.trim().replace(/^"|"$/g, '').replace(/^

"/, '

').replace(/"<\/p>$/, '

'); // strip quotation marks + let fullTextHTMLAtAssignment = this.fullTextHTML.trim().replace(/^"|"$/g, '').replace(/^

"/, '

').replace(/"<\/p>$/, '

'); // strip quotation marks + if(this.plainTextMode) { + fullTextHTMLAtAssignment = stripHtmlKeepLines(fullTextHTMLAtAssignment); + } //console.log(">>>>>>>>>>>> fullTextHTMLAtAssignment: " + fullTextHTMLAtAssignment); actionButton.addEventListener('click', async () => { if(promptData.mailMessageId == -1) { // we are using the reply from the compose window! diff --git a/js/mzta-utils.js b/js/mzta-utils.js index 23142a85..e02e6070 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -175,6 +175,16 @@ export function sanitizeHtml(input) { return input.replace(/<(?!br\s*\/?)[^>]+>/gi, ''); } +export function stripHtmlKeepLines(htmlString) { + // Replaces

tags with a newline at the beginning + // and removes all other HTML tags + return htmlString + .replace(/

/gi, '') // removes

tags + .replace(/<\/p>/gi, '\n') // replaces

tags with newline + .replace(/<[^>]*>/g, '') // removes any other HTML tags + .trim(); // removes leading/trailing whitespace +} + // This method is used to convert the model string id used in the URL // to the model string used in the webpage export function getGPTWebModelString(model) { diff --git a/mzta-background.js b/mzta-background.js index aaeaa2fc..674ce236 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -20,7 +20,7 @@ import { mzta_script } from './js/mzta-chatgpt.js'; import { prefs_default } from './options/mzta-options-default.js'; import { mzta_Menus } from './js/mzta-menus.js'; import { taLogger } from './js/mzta-logger.js'; -import { getCurrentIdentity, getOriginalBody, replaceBody, setBody, i18nConditionalGet, generateCallID, migrateCustomPromptsStorage, migrateDefaultPromptsPropStorage, getGPTWebModelString, getTagsList, createTag, assignTagsToMessage, checkIfTagExists, getActiveSpecialPromptsIDs, checkSparksPresence, getMessages, getMailBody, extractJsonObject, contextMenuID_AddTags, contextMenuID_Spamfilter } from './js/mzta-utils.js'; +import { getCurrentIdentity, getOriginalBody, replaceBody, setBody, i18nConditionalGet, generateCallID, migrateCustomPromptsStorage, migrateDefaultPromptsPropStorage, getGPTWebModelString, getTagsList, createTag, assignTagsToMessage, checkIfTagExists, getActiveSpecialPromptsIDs, checkSparksPresence, getMessages, getMailBody, extractJsonObject, contextMenuID_AddTags, contextMenuID_Spamfilter, stripHtmlKeepLines } from './js/mzta-utils.js'; import { taPromptUtils } from './js/mzta-utils-prompt.js'; import { mzta_specialCommand } from './js/mzta-special-commands.js'; import { getSpamFilterPrompt } from './js/mzta-prompts.js'; @@ -200,9 +200,12 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { return _replaceSelectedText(message.tabId, message.text); case 'chatgpt_replyMessage': async function _replyMessage(message) { - const paragraphsHtmlString = message.text; + let paragraphsHtmlString = message.text; //console.log(">>>>>>>>>>>> paragraphsHtmlString: " + paragraphsHtmlString); - let prefs = await browser.storage.sync.get({reply_type: 'reply_all'}); + let prefs = await browser.storage.sync.get({reply_type: prefs_default.reply_type, composing_plain_text: prefs_default.composing_plain_text}); + if(prefs.composing_plain_text){ + paragraphsHtmlString = stripHtmlKeepLines(paragraphsHtmlString); + } //console.log('reply_type: ' + prefs.reply_type); let replyType = 'replyToAll'; if(prefs.reply_type === 'reply_sender'){