diff --git a/messageDisplay/message-content-script.js b/messageDisplay/message-content-script.js new file mode 100644 index 00000000..30fc5763 --- /dev/null +++ b/messageDisplay/message-content-script.js @@ -0,0 +1,105 @@ +async function showSummaryPane() { + // Create the summary pane element + const summaryPane = document.createElement("div"); + summaryPane.className = "thunderai-summary-pane"; + + // Create the title element + const summaryTitle = document.createElement("div"); + summaryTitle.className = "thunderai-summary-title"; + summaryTitle.innerText = "ThunderAI Summary"; + + // Create a loading indicator + const loadingIndicator = document.createElement("div"); + loadingIndicator.className = "thunderai-summary-content"; + loadingIndicator.innerText = "Generating AI summary..."; + + // Create the content element (initially hidden) + const summaryContent = document.createElement("div"); + summaryContent.className = "thunderai-summary-content"; + summaryContent.style.display = 'none'; + + // Add title and loading indicator to the pane + summaryPane.appendChild(summaryTitle); + summaryPane.appendChild(loadingIndicator); + summaryPane.appendChild(summaryContent); + + // Insert it as the very first element in the message + document.body.insertBefore(summaryPane, document.body.firstChild); + + // Get the message content and generate summary + try { + const messageContent = getMessageContent(); + const aiSummary = await generateAISummary(messageContent); + + // Update the UI with the AI summary + loadingIndicator.style.display = 'none'; + summaryContent.innerText = aiSummary; + summaryContent.style.display = 'block'; + } catch (error) { + console.error("Error generating AI summary:", error); + loadingIndicator.innerText = "Failed to generate AI summary. Showing message preview instead."; + loadingIndicator.style.color = '#d70022'; + + // Fallback to showing truncated message content + const messageContent = getMessageContent(); + loadingIndicator.innerText += "\n\n" + truncateMessageContent(messageContent); + } +} + +function getMessageContent() { + // Get the main message content from the page + // This selects the main message body content + const messageBody = document.querySelector('.moz-text-flowed, .moz-text-plain, body'); + if (messageBody) { + return messageBody.textContent || messageBody.innerText || ''; + } + + // Fallback: get the entire body content + return document.body.textContent || document.body.innerText || ''; +} + +function truncateMessageContent(content) { + // Clean up the content by removing excessive whitespace and newlines + const cleanedContent = content.replace(/\s+/g, ' ').trim(); + + // Truncate to a reasonable length for preview + const maxLength = 500; + if (cleanedContent.length <= maxLength) { + return cleanedContent; + } + + return cleanedContent.substring(0, maxLength) + '...'; +} + +async function generateAISummary(messageContent) { + // Clean up the message content + const cleanedContent = messageContent.replace(/\s+/g, ' ').trim(); + + // Create a simple summary prompt + const summaryPrompt = `Please provide a concise summary of the following email message. The summary should be 3-5 sentences maximum and capture the main points: + +${cleanedContent} + +Summary:`; + + // Request AI summary from the background script + return new Promise((resolve, reject) => { + // Send message to background script to get AI summary + browser.runtime.sendMessage({ + command: "generate_summary", + content: cleanedContent, + prompt: summaryPrompt + }, (response) => { + if (response && response.summary) { + resolve(response.summary); + } else if (response && response.error) { + reject(new Error(response.error)); + } else { + reject(new Error("Failed to get AI summary")); + } + }); + }); +} + +// Call the function to show the pane +showSummaryPane(); \ No newline at end of file diff --git a/messageDisplay/message-content-styles.css b/messageDisplay/message-content-styles.css new file mode 100644 index 00000000..6a4019c8 --- /dev/null +++ b/messageDisplay/message-content-styles.css @@ -0,0 +1,20 @@ +.thunderai-summary-pane { + background-color: #f0f0f0; + color: #333; + font-weight: 400; + padding: 0.5rem; + margin-bottom: 1rem; + border-radius: 4px; + border: 1px solid #ddd; +} + +.thunderai-summary-title { + font-weight: bold; + margin-bottom: 0.5rem; + color: #d70022; +} + +.thunderai-summary-content { + font-size: 0.9rem; + line-height: 1.4; +} \ No newline at end of file diff --git a/mzta-background.js b/mzta-background.js index e1d63747..c0199a7d 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -99,6 +99,13 @@ browser.composeScripts.register({ // Register the message display script for all newly opened message tabs. messenger.messageDisplayScripts.register({ js: [{ file: "js/mzta-compose-script.js" }], + css: [{ file: "messageDisplay/message-content-styles.css" }] +}); + +// Register our new ThunderAI summary script +messenger.messageDisplayScripts.register({ + js: [{ file: "messageDisplay/message-content-script.js" }], + css: [{ file: "messageDisplay/message-content-styles.css" }] }); // Inject script and CSS in all already open message tabs. @@ -114,6 +121,13 @@ for (let messageTab of messageTabs) { await browser.tabs.executeScript(messageTab.id, { file: "js/mzta-compose-script.js" }) + // Inject our ThunderAI summary script + await browser.tabs.executeScript(messageTab.id, { + file: "messageDisplay/message-content-script.js" + }) + await browser.tabs.insertCSS(messageTab.id, { + file: "messageDisplay/message-content-styles.css" + }); } catch (error) { console.error("[ThunderAI] Error injecting message display script:", error); console.error("[ThunderAI] Message tab:", messageTab.url); @@ -220,6 +234,31 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { // handler function. if (message && message.hasOwnProperty("command")){ switch (message.command) { + case 'generate_summary': + async function _generate_summary(message) { + try { + // Get user preferences for AI connection + let prefs = await browser.storage.sync.get({ + connection_type: prefs_default.connection_type, + chatgpt_model: prefs_default.chatgpt_model, + chatgpt_api_key: prefs_default.chatgpt_api_key, + do_debug: prefs_default.do_debug + }); + + // Use the existing ThunderAI infrastructure + const summary = await generateAISummaryUsingThunderAIInfrastructure( + message.content, + message.prompt, + prefs + ); + + return { summary: summary }; + } catch (error) { + console.error("[ThunderAI] Error generating summary:", error); + return { error: "Failed to generate AI summary: " + error.message }; + } + } + return _generate_summary(message); // case 'chatgpt_open': // openChatGPT(message.prompt,message.action,message.tabId); // return true; @@ -1177,3 +1216,48 @@ try { taLog.log("Using browser.messages.onNewMailReceived.addListener with one agrument for Thunderbird 115."); browser.messages.onNewMailReceived.addListener(newEmailListener); } + +/** + * AI summary generation function using ThunderAI infrastructure + */ +async function generateAISummaryUsingThunderAIInfrastructure(content, prompt, prefs) { + // Import the special command class + const { mzta_specialCommand } = await import('./js/mzta-special-commands.js'); + + // Determine which LLM to use based on user preferences + const llmType = getConnectionType(prefs.connection_type, {}, ''); + + // Create a special command instance + const summaryCommand = new mzta_specialCommand({ + prompt: prompt, + llm: llmType, + custom_model: prefs.chatgpt_model, + do_debug: prefs.do_debug + }); + + // Initialize the worker + await summaryCommand.initWorker(); + + // Send the prompt and get the AI response + const aiResponse = await summaryCommand.sendPrompt(); + + // Clean up the response - extract just the summary content + const cleanedResponse = cleanAISummaryResponse(aiResponse); + + return cleanedResponse; +} + +/** + * Helper function to clean AI response + */ +function cleanAISummaryResponse(response) { + // Remove any markdown formatting or code blocks + let cleaned = response.replace(/```[\s\S]*?```/g, ''); + cleaned = cleaned.replace(/[\*#_~`]/g, ''); + cleaned = cleaned.replace(/\s+/g, ' ').trim(); + + // Remove any "Summary:" prefixes that the AI might add + cleaned = cleaned.replace(/^Summary:\s*/i, ''); + + return cleaned; +}