First working version
This commit is contained in:
parent
e6363e7af2
commit
41e4a2e547
21 changed files with 885 additions and 0 deletions
111
_locales/en/messages.json
Normal file
111
_locales/en/messages.json
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
{
|
||||||
|
"extensionDescription": {
|
||||||
|
"message": "Use ChatGPT to enhance you emails!",
|
||||||
|
"description": "Description of the extension."
|
||||||
|
},
|
||||||
|
|
||||||
|
"menu_title": {
|
||||||
|
"message": "AI",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_lang": {
|
||||||
|
"message": "Replay in ",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_translate_lang": {
|
||||||
|
"message": "english.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_reply": {
|
||||||
|
"message": "Reply to this",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_rewrite_polite": {
|
||||||
|
"message": "Rewrite polite",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_rewrite_formal": {
|
||||||
|
"message": "Rewrite formal",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_classify": {
|
||||||
|
"message": "Classify",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_summarize_this": {
|
||||||
|
"message": "Summarize this",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_translate_this": {
|
||||||
|
"message": "Translate this",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_selection_needed": {
|
||||||
|
"message": "To proceed, you need to select some text!",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"chatgpt_win_working": {
|
||||||
|
"message": "Work in progress...",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"chatgpt_win_job_completed": {
|
||||||
|
"message": "Completed!",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"chatgpt_win_get_answer": {
|
||||||
|
"message": "Use last answer",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"chatgpt_win_close": {
|
||||||
|
"message": "Close",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsInfoTitle": {
|
||||||
|
"message": "Important Information",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsInfoDesc_1": {
|
||||||
|
"message": "It's not always possibile to get the right file type from the email received by Sharepoint or OneDrive.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsInfoDesc_2": {
|
||||||
|
"message": "If this extension can't understand the file type, it will show four buttons: one to use Word, one to use Excel, one to use PowerPoint and one to open the file in the browser.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsInfoDesc_3": {
|
||||||
|
"message": "The option to force to use Microsoft Edge is for a scenario where you do not use Edge as the default browser, but is the browser used with your Business account.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsDonation_1": {
|
||||||
|
"message": "Do you like this addon?",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsDonation_2": {
|
||||||
|
"message": "Consider to make a donation!",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"backToOptionsText": {
|
||||||
|
"message": "Options",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
111
_locales/it/messages.json
Normal file
111
_locales/it/messages.json
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
{
|
||||||
|
"extensionDescription": {
|
||||||
|
"message": "Apri un condiviso di un file di SharePoint o OneDrive direttamente con l'applicazione corretta, invece che il browser.",
|
||||||
|
"description": "Descrizione dell'estensione."
|
||||||
|
},
|
||||||
|
|
||||||
|
"menu_title": {
|
||||||
|
"message": "IA",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_lang": {
|
||||||
|
"message": "Rispondi in ",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_translate_lang": {
|
||||||
|
"message": "italiano.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_reply": {
|
||||||
|
"message": "Rispondi",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_rewrite_polite": {
|
||||||
|
"message": "Riscrivi cortese",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_rewrite_formal": {
|
||||||
|
"message": "Riscrivi formale",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_classify": {
|
||||||
|
"message": "Classifica",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_summarize_this": {
|
||||||
|
"message": "Riassumi",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_translate_this": {
|
||||||
|
"message": "Traduci",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_selection_needed": {
|
||||||
|
"message": "Per procedere è necessario che selezioni del testo!",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"chatgpt_win_working": {
|
||||||
|
"message": "Elaborazione in corso...",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"chatgpt_win_job_completed": {
|
||||||
|
"message": "Completato!",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"chatgpt_win_get_answer": {
|
||||||
|
"message": "Usa l'ultima risposta",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"chatgpt_win_close": {
|
||||||
|
"message": "Chiudi",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsInfoTitle": {
|
||||||
|
"message": "Informazioni Importanti",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsInfoDesc_1": {
|
||||||
|
"message": "Non è sempre possibile capire il tupo di file dalla mail ricevuta da SharePoint o OneDrive.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsInfoDesc_2": {
|
||||||
|
"message": "Se questa estensione non riesce a riconoscere il tipo del file, mostrerà quattro pulsanti: uno per usare Word, uno per Excel, uno per PowerPoint e uno per aprire il link nel browser.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsInfoDesc_3": {
|
||||||
|
"message": "L'opzione di forzare l'apertura in Microsoft Edge è per lo scenario in cui non stai usando Edge come browser predefinito, ma è il browser con il tuo account aziendale.",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsDonation_1": {
|
||||||
|
"message": "Ti piace questo componente aggiuntivo?",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"prefsDonation_2": {
|
||||||
|
"message": "Considera di fare una donazione!",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
|
||||||
|
"backToOptionsText": {
|
||||||
|
"message": "Opzioni",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
images/icon-16px.png
Normal file
BIN
images/icon-16px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 802 B |
BIN
images/icon-32px.png
Normal file
BIN
images/icon-32px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
images/icon.png
Normal file
BIN
images/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
156
js/mzta-chatgpt.js
Normal file
156
js/mzta-chatgpt.js
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
// Original methods derived from https://github.com/KudoAI/chatgpt.js/blob/7eb8463cd61143fa9e1d5a8ec3c14d3c1b286e54/chatgpt.js
|
||||||
|
// Using a full string to inject it in the chatgpt page to avoid any security error
|
||||||
|
|
||||||
|
export const mzta_script = `
|
||||||
|
async function chatgpt_sendMsg(msg, method ='') {
|
||||||
|
const textArea = document.querySelector('form textarea'),
|
||||||
|
sendButton = document.querySelector('form button[class*="bottom"]');
|
||||||
|
textArea.value = msg;
|
||||||
|
textArea.dispatchEvent(new Event('input', { bubbles: true })); // enable send button
|
||||||
|
const delaySend = setInterval(() => {
|
||||||
|
if (!sendButton?.hasAttribute('disabled')) { // send msg
|
||||||
|
method.toLowerCase() == 'click' ? sendButton.click()
|
||||||
|
: textArea.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 13, bubbles: true }));
|
||||||
|
clearInterval(delaySend);
|
||||||
|
}
|
||||||
|
}, 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function chatgpt_isIdle() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
if (chatgpt_getRegenerateButton()) {
|
||||||
|
clearInterval(intervalId); resolve(true);
|
||||||
|
}}, 100);});}
|
||||||
|
|
||||||
|
function chatgpt_getRegenerateButton() {
|
||||||
|
for (const mainSVG of document.querySelectorAll('main svg')) {
|
||||||
|
if (mainSVG.querySelector('path[d*="M4.5 2.5C5.05228"]')) // regen icon found
|
||||||
|
return mainSVG.parentNode.parentNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function chatgpt_getFromDOM(pos) {
|
||||||
|
const responseDivs = document.querySelectorAll('div[data-testid*="conversation-turn"]:nth-child(odd)'),
|
||||||
|
strPos = pos.toString().toLowerCase();
|
||||||
|
let response = '';
|
||||||
|
if (responseDivs.length) {
|
||||||
|
if (/last|final/.test(strPos)) // get last response
|
||||||
|
response = responseDivs[responseDivs.length - 1].textContent;
|
||||||
|
else { // get nth response
|
||||||
|
const nthOfResponse = (
|
||||||
|
|
||||||
|
// Calculate base number
|
||||||
|
Number.isInteger(pos) ? pos : // do nothing for integers
|
||||||
|
/^\d+/.test(strPos) ? /^\d+/.exec(strPos)[0] : // extract first digits for strings w/ them
|
||||||
|
( // convert words to integers for digitless strings
|
||||||
|
/^(?:1|one|fir)(?:st)?$/.test(strPos) ? 1
|
||||||
|
: /^(?:2|tw(?:o|en|el(?:ve|f))|seco)(?:nd|t[yi])?(?:e?th)?$/.test(strPos) ? 2
|
||||||
|
: /^(?:3|th(?:ree|ir?))(?:rd|teen|t[yi])?(?:e?th)?$/.test(strPos) ? 3
|
||||||
|
: /^(?:4|fou?r)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 4
|
||||||
|
: /^(?:5|fi(?:ve|f))(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 5
|
||||||
|
: /^(?:6|six)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 6
|
||||||
|
: /^(?:7|seven)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 7
|
||||||
|
: /^(?:8|eight?)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 8
|
||||||
|
: /^(?:9|nine?)(?:teen|t[yi])?(?:e?th)?$/.test(strPos) ? 9
|
||||||
|
: /^(?:10|ten)(?:th)?$/.test(strPos) ? 10 : 1 )
|
||||||
|
|
||||||
|
// Transform base number if suffixed
|
||||||
|
* ( /(ty|ieth)$/.test(strPos) ? 10 : 1 ) // x 10 if -ty/ieth
|
||||||
|
+ ( /teen(th)?$/.test(strPos) ? 10 : 0 ) // + 10 if -teen/teenth
|
||||||
|
|
||||||
|
);
|
||||||
|
response = responseDivs[nthOfResponse - 1].textContent;
|
||||||
|
}
|
||||||
|
response = response.replace(/^ChatGPTChatGPT/, ''); // strip sender name
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function chatpgt_scrollToBottom () {
|
||||||
|
try { document.querySelector('button[class*="cursor"][class*="bottom"]').click(); }
|
||||||
|
catch (err) { console.error('', err); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCustomDiv(prompt_action,tabId) {
|
||||||
|
// Create <style> element for the CSS
|
||||||
|
var style = document.createElement('style');
|
||||||
|
style.innerHTML = ".mzta-header-fixed {position: fixed;top: 0;left: 0;height:100px;width: 100%;background-color: #333;color: white;text-align: center;padding: 10px 0;z-index: 1000;}"
|
||||||
|
style.innerHTML += "body {padding-top: 100px;}";
|
||||||
|
style.innerHTML += "#mzta-ok_btn {background-color: #007bff;border: none;color: white;padding: 8px 15px;text-align: center;text-decoration: none;display: inline-block;font-size: 16px;margin: 4px 2px;transition-duration: 0.4s;cursor: pointer;border-radius: 5px;}";
|
||||||
|
style.innerHTML += "#mzta-ok_btn:hover {background-color: #0056b3;color: white;}";
|
||||||
|
style.innerHTML += "#mzta-curr_msg{}";
|
||||||
|
// Add <style> to the page's <head>
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
// Fixed div
|
||||||
|
var divFisso = document.createElement('div');
|
||||||
|
divFisso.classList.add('mzta-header-fixed');
|
||||||
|
divFisso.textContent = '';
|
||||||
|
|
||||||
|
// span for the text
|
||||||
|
var curr_msg = document.createElement('span');
|
||||||
|
curr_msg.id='mzta-curr_msg';
|
||||||
|
curr_msg.innerHTML = browser.i18n.getMessage("chatgpt_win_working");
|
||||||
|
divFisso.appendChild(curr_msg);
|
||||||
|
|
||||||
|
var pulsante = document.createElement('button');
|
||||||
|
pulsante.id="mzta-ok_btn";
|
||||||
|
//console.log('default: '+prompt_action)
|
||||||
|
switch(prompt_action){
|
||||||
|
default:
|
||||||
|
case 0: // close window
|
||||||
|
pulsante.innerHTML = browser.i18n.getMessage("chatgpt_win_close");
|
||||||
|
pulsante.onclick = async function() {
|
||||||
|
browser.runtime.sendMessage({command: "chatgpt_close"});
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 1: // do reply
|
||||||
|
pulsante.innerHTML = browser.i18n.getMessage("chatgpt_win_get_answer");
|
||||||
|
pulsante.onclick = async function() { // TODO
|
||||||
|
const response = await chatgpt_getFromDOM('last');
|
||||||
|
//console.log(response);
|
||||||
|
browser.runtime.sendMessage({command: "chatgpt_close"});
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 2: // replace text
|
||||||
|
pulsante.innerHTML = browser.i18n.getMessage("chatgpt_win_get_answer");
|
||||||
|
pulsante.onclick = async function() { // TODO
|
||||||
|
const response = await chatgpt_getFromDOM('last');
|
||||||
|
//console.log('replace text: '+tabId)
|
||||||
|
browser.runtime.sendMessage({command: "chatgpt_replaceSelectedText", text: response, tabId: tabId});
|
||||||
|
//console.log(response);
|
||||||
|
browser.runtime.sendMessage({command: "chatgpt_close"});
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pulsante.style.display = 'none';
|
||||||
|
divFisso.appendChild(pulsante);
|
||||||
|
|
||||||
|
document.body.insertBefore(divFisso, document.body.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
function operation_done(){
|
||||||
|
document.getElementById('mzta-curr_msg').innerHTML = browser.i18n.getMessage("chatgpt_win_job_completed")+"<br>";
|
||||||
|
document.getElementById('mzta-ok_btn').style.display = 'inline';
|
||||||
|
chatpgt_scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Nello script di contenuto
|
||||||
|
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||||
|
if (message.command === "chatgpt_send") {
|
||||||
|
addCustomDiv(message.action,message.tabId);
|
||||||
|
//console.log(message.prompt);
|
||||||
|
(async () => {
|
||||||
|
//await chatgpt.isLoaded();
|
||||||
|
await chatgpt_sendMsg(message.prompt,'click');
|
||||||
|
await chatgpt_isIdle();
|
||||||
|
// console.log(response);
|
||||||
|
operation_done();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
`
|
||||||
76
js/mzta-compose-script.js
Normal file
76
js/mzta-compose-script.js
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Modified version derived from https://github.com/ali-raheem/Aify/blob/13ff87583bc520fb80f555ab90a90c5c9df797a7/plugin/content_scripts/compose.js
|
||||||
|
|
||||||
|
const makeParagraphs = (text, func) => {
|
||||||
|
const chunks = text.split(/\n{2,}/);
|
||||||
|
if (chunks.length == 1) {
|
||||||
|
return func(document.createTextNode(text));
|
||||||
|
}
|
||||||
|
const paragraphs = chunks.map((t) => {
|
||||||
|
const p = document.createElement("p");
|
||||||
|
p.innerText = t;
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
for (let i = paragraphs.length - 1; i >= 0; i--) {
|
||||||
|
func(paragraphs[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const insert = function (text) {
|
||||||
|
const prefix = window.document.body.getElementsByClassName("moz-cite-prefix");
|
||||||
|
if (prefix.length > 0) {
|
||||||
|
const divider = prefix[0];
|
||||||
|
let sibling = divider.previousSibling;
|
||||||
|
while (sibling) {
|
||||||
|
window.document.body.removeChild(sibling);
|
||||||
|
sibling = divider.previousSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return makeParagraphs(text, function (p) {
|
||||||
|
window.document.body.insertBefore(p, window.document.body.firstChild);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
browser.runtime.onMessage.addListener((message) => {
|
||||||
|
switch (message.command) {
|
||||||
|
case "getSelectedText":
|
||||||
|
return Promise.resolve(window.getSelection().toString());
|
||||||
|
|
||||||
|
case "replaceSelectedText":
|
||||||
|
const selectedText = window.getSelection().toString();
|
||||||
|
if (selectedText === '') {
|
||||||
|
return insert(message.text);
|
||||||
|
}
|
||||||
|
const sel = window.getSelection();
|
||||||
|
if (!sel || sel.type !== "Range" || !sel.rangeCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const r = sel.getRangeAt(0);
|
||||||
|
r.deleteContents();
|
||||||
|
makeParagraphs(message.text, function (p) {
|
||||||
|
r.insertNode(p);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "getText":
|
||||||
|
let t = '';
|
||||||
|
const children = window.document.body.childNodes;
|
||||||
|
for (const node of children) {
|
||||||
|
if (node instanceof Element) {
|
||||||
|
if (node.classList.contains('moz-signature')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t += node.textContent;
|
||||||
|
}
|
||||||
|
return Promise.resolve(t);
|
||||||
|
|
||||||
|
case "getTextOnly":
|
||||||
|
return Promise.resolve(window.document.body.innerText);
|
||||||
|
|
||||||
|
default:
|
||||||
|
// do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
67
js/mzta-i18n.js
Normal file
67
js/mzta-i18n.js
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* This file is provided by the addon-developer-support repository at
|
||||||
|
* https://github.com/thundernest/addon-developer-support
|
||||||
|
*
|
||||||
|
* For usage descriptions, please check:
|
||||||
|
* https://github.com/thundernest/addon-developer-support/tree/master/scripts/i18n
|
||||||
|
*
|
||||||
|
* Version 1.1
|
||||||
|
*
|
||||||
|
* Derived from:
|
||||||
|
* http://github.com/piroor/webextensions-lib-l10n
|
||||||
|
*
|
||||||
|
* Original license:
|
||||||
|
* The MIT License, Copyright (c) 2016-2019 YUKI "Piro" Hiroshi
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
var i18n = {
|
||||||
|
updateString(string) {
|
||||||
|
let re = new RegExp(this.keyPrefix + "(.+?)__", "g");
|
||||||
|
return string.replace(re, (matched) => {
|
||||||
|
const key = matched.slice(this.keyPrefix.length, -2);
|
||||||
|
let rv = this.extension
|
||||||
|
? this.extension.localeData.localizeMessage(key)
|
||||||
|
: messenger.i18n.getMessage(key);
|
||||||
|
return rv || matched;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateSubtree(node) {
|
||||||
|
const texts = document.evaluate(
|
||||||
|
'descendant::text()[contains(self::text(), "' + this.keyPrefix + '")]',
|
||||||
|
node,
|
||||||
|
null,
|
||||||
|
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
for (let i = 0, maxi = texts.snapshotLength; i < maxi; i++) {
|
||||||
|
const text = texts.snapshotItem(i);
|
||||||
|
if (text.nodeValue.includes(this.keyPrefix))
|
||||||
|
text.nodeValue = this.updateString(text.nodeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
const attributes = document.evaluate(
|
||||||
|
'descendant::*/attribute::*[contains(., "' + this.keyPrefix + '")]',
|
||||||
|
node,
|
||||||
|
null,
|
||||||
|
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
for (let i = 0, maxi = attributes.snapshotLength; i < maxi; i++) {
|
||||||
|
const attribute = attributes.snapshotItem(i);
|
||||||
|
if (attribute.value.includes(this.keyPrefix))
|
||||||
|
attribute.value = this.updateString(attribute.value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateDocument(options = {}) {
|
||||||
|
this.extension = null;
|
||||||
|
this.keyPrefix = "__MSG_";
|
||||||
|
if (options) {
|
||||||
|
if (options.extension) this.extension = options.extension;
|
||||||
|
if (options.keyPrefix) this.keyPrefix = options.keyPrefix;
|
||||||
|
}
|
||||||
|
this.updateSubtree(document);
|
||||||
|
},
|
||||||
|
};
|
||||||
25
js/mzta-prompts.js
Normal file
25
js/mzta-prompts.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Modified version derived from https://github.com/ali-raheem/Aify/blob/13ff87583bc520fb80f555ab90a90c5c9df797a7/plugin/html/globals.js
|
||||||
|
|
||||||
|
/* Types (type attribute):
|
||||||
|
0: always show (when composing a part of the text must be selected if need_selected = 1)
|
||||||
|
1: show when reading an email
|
||||||
|
2: show when composing a mail (a part of the text must be selected if need_selected = 1)
|
||||||
|
|
||||||
|
Actions (action attribute):
|
||||||
|
0: close button
|
||||||
|
1: do reply
|
||||||
|
2: substitute text
|
||||||
|
|
||||||
|
Only if text selected (attribute need_selected):
|
||||||
|
0: no selection needed (use all the message body)
|
||||||
|
1: need a selection
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const defaultPrompts = [
|
||||||
|
{ name: "__MSG_prompt_reply__", text: "Reply to the following email.", type: 1, action: 1, need_selected: 0 },
|
||||||
|
{ name: "__MSG_prompt_rewrite_polite__", text: "Rewrite the following text to be more polite. Reply with only the re-written text and with no extra comments or other text.", type: 2, action: 2, need_selected: 1 },
|
||||||
|
{ name: "__MSG_prompt_rewrite_formal__", text: "Rewrite the following text to be more formal. Reply with only the re-written text and with no extra comments or other text.", type: 2, action: 2, need_selected: 1 },
|
||||||
|
{ name: "__MSG_prompt_classify__", text: "Classify the following text in terms of Politeness, Warmth, Formality, Assertiveness, Offensiveness giving a percentage for each category. Reply with only the category and score with no extra comments or other text.", type: 0, action: 0, need_selected: 0 },
|
||||||
|
{ name: "__MSG_prompt_summarize_this__", text: "Summerize the following email into a bullet point list.", type: 0, action: 0, need_selected: 0 },
|
||||||
|
{ name: "__MSG_prompt_translate_this__", text: "Translate the following email in __MSG_prompt_translate_lang__", type: 0, action: 0, need_selected: 0 },
|
||||||
|
];
|
||||||
45
manifest.json
Normal file
45
manifest.json
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
{
|
||||||
|
"manifest_version": 2,
|
||||||
|
"name": "ThunderAI",
|
||||||
|
"description": "__MSG_extensionDescription__",
|
||||||
|
"version": "0.1",
|
||||||
|
"author": "Mic (m@micz.it)",
|
||||||
|
"browser_specific_settings": {
|
||||||
|
"gecko": {
|
||||||
|
"id": "thunderai@micz.it",
|
||||||
|
"strict_min_version": "115.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"message_display_action": {
|
||||||
|
"default_popup": "messageMenu/mzta-msgmenu.html",
|
||||||
|
"default_title": "__MSG_menu_title__",
|
||||||
|
"default_icon": "images/icon-32px.png"
|
||||||
|
},
|
||||||
|
"compose_action": {
|
||||||
|
"default_popup": "messageMenu/mzta-msgmenu.html",
|
||||||
|
"default_title": "__MSG_menu_title__",
|
||||||
|
"default_icon": "images/icon-32px.png"
|
||||||
|
},
|
||||||
|
"permissions": [
|
||||||
|
"compose",
|
||||||
|
"messagesRead",
|
||||||
|
"storage",
|
||||||
|
"menus",
|
||||||
|
"messagesModify",
|
||||||
|
"tabs",
|
||||||
|
"activeTab",
|
||||||
|
"<all_urls>"
|
||||||
|
],
|
||||||
|
"background": {
|
||||||
|
"page": "mzta-background.html"
|
||||||
|
},
|
||||||
|
"icons": {
|
||||||
|
"64": "images/icon.png",
|
||||||
|
"32": "images/icon-32px.png",
|
||||||
|
"16": "images/icon-16px.png"
|
||||||
|
},
|
||||||
|
"default_locale": "en",
|
||||||
|
"options_ui": {
|
||||||
|
"page": "options/mzta-options.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
16
messageMenu/mzta-msgmenu.css
Normal file
16
messageMenu/mzta-msgmenu.css
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
.grid-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 6fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
font-weight: bold
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container div {
|
||||||
|
margin: 1ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
13
messageMenu/mzta-msgmenu.html
Normal file
13
messageMenu/mzta-msgmenu.html
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>ChatGPT</title>
|
||||||
|
<link rel="stylesheet" type="text/css" media="screen" href="mzta-msgmenu.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="prompts-container"></div>
|
||||||
|
<script type="module" src="mzta-msgmenu.js"></script>
|
||||||
|
<script src="../js/mzta-i18n.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
65
messageMenu/mzta-msgmenu.js
Normal file
65
messageMenu/mzta-msgmenu.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Modified version derived from https://github.com/ali-raheem/Aify/blob/cfadf52f576b7be3720b5b73af7c8d3129c054da/plugin/html/actions.js
|
||||||
|
|
||||||
|
import { defaultPrompts } from '../js/mzta-prompts.js';
|
||||||
|
|
||||||
|
const addAction = (curr_prompt, promptsContainer) => {
|
||||||
|
const promptDiv = document.createElement("div");
|
||||||
|
promptDiv.classList.add("button");
|
||||||
|
const nameInput = document.createElement("p");
|
||||||
|
nameInput.classList.add("flat");
|
||||||
|
nameInput.innerText = curr_prompt.name;
|
||||||
|
nameInput.classList.add("prompt-name");
|
||||||
|
|
||||||
|
const getHighlight = async () => {
|
||||||
|
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||||
|
return {tabId: tabs[0].id,
|
||||||
|
selection: await browser.tabs.sendMessage(tabs[0].id, { command: "getSelectedText" }),
|
||||||
|
text: await browser.tabs.sendMessage(tabs[0].id, { command: "getTextOnly" })
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nameInput.onclick = async () => {
|
||||||
|
const msg_text = await getHighlight();
|
||||||
|
|
||||||
|
//check if a selection is needed
|
||||||
|
if(curr_prompt.need_selected && (msg_text.selection==='')){
|
||||||
|
//A selection is needed, but nothing is selected!
|
||||||
|
alert(browser.i18n.getMessage('prompt_selection_needed'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var body_text = '';
|
||||||
|
if (msg_text.selection!=='') {
|
||||||
|
body_text = msg_text.selection.replace(/\s+/g, ' ').trim();
|
||||||
|
} else {
|
||||||
|
body_text = msg_text.text.replace(/\s+/g, ' ').trim();
|
||||||
|
}
|
||||||
|
//open chatgpt window
|
||||||
|
//console.log("Click menu item...");
|
||||||
|
var fullPrompt = curr_prompt.text + " " + browser.i18n.getMessage("prompt_lang") + browser.i18n.getMessage("prompt_translate_lang") + " \"" + msg_text.text + "\" ";
|
||||||
|
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||||
|
browser.runtime.sendMessage({command: "chatgpt_open", prompt: fullPrompt, action: curr_prompt.action, tabId: tabs[0].id});
|
||||||
|
};
|
||||||
|
promptDiv.appendChild(nameInput);
|
||||||
|
promptsContainer.appendChild(promptDiv);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function checkCompose(){
|
||||||
|
return await browser.windows.getCurrent().then((currWindow) => {
|
||||||
|
//console.log("currWindow.type: "+currWindow.type);
|
||||||
|
return currWindow.type == 'messageCompose';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
|
let inCompose = await checkCompose();
|
||||||
|
//console.log("inCompose: "+inCompose)
|
||||||
|
const actionsContainer = document.getElementById("prompts-container");
|
||||||
|
defaultPrompts.forEach((prompt) => {
|
||||||
|
if((prompt.type == 0)||((prompt.type == 1)&&(!inCompose))||((prompt.type == 2)&&(inCompose))){
|
||||||
|
addAction(prompt, actionsContainer)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
i18n.updateDocument();
|
||||||
|
});
|
||||||
|
|
||||||
9
mzta-background.html
Normal file
9
mzta-background.html
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<script src="mzta-background.js" type="module"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
</html>
|
||||||
75
mzta-background.js
Normal file
75
mzta-background.js
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { mzta_script } from './js/mzta-chatgpt.js';
|
||||||
|
|
||||||
|
var createdWindowID = null;
|
||||||
|
|
||||||
|
browser.composeScripts.register({
|
||||||
|
js: [{file: "/js/mzta-compose-script.js"}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register the message display script for all newly opened message tabs.
|
||||||
|
messenger.messageDisplayScripts.register({
|
||||||
|
js: [{ file: "js/mzta-compose-script.js" }],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Inject script and CSS in all already open message tabs.
|
||||||
|
let openTabs = await messenger.tabs.query();
|
||||||
|
let messageTabs = openTabs.filter(
|
||||||
|
tab => ["mail", "messageDisplay"].includes(tab.type)
|
||||||
|
);
|
||||||
|
for (let messageTab of messageTabs) {
|
||||||
|
browser.tabs.executeScript(messageTab.id, {
|
||||||
|
file: "js/mzta-compose-script.js"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||||
|
// Check what type of message we have received and invoke the appropriate
|
||||||
|
// handler function.
|
||||||
|
if (message && message.hasOwnProperty("command")){
|
||||||
|
switch (message.command) {
|
||||||
|
case 'chatgpt_open':
|
||||||
|
openChatGPT(message.prompt,message.action,message.tabId);
|
||||||
|
return true;
|
||||||
|
case 'chatgpt_close':
|
||||||
|
browser.windows.remove(createdWindowID).then(() => {
|
||||||
|
console.log("ChatGPT window closed successfully.");
|
||||||
|
return true;
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error("Error closing ChatGPT window:", error);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'chatgpt_replaceSelectedText':
|
||||||
|
//console.log('chatgpt_replaceSelectedText: [' + message.tabId +'] ' + message.text)
|
||||||
|
browser.tabs.sendMessage(message.tabId, { command: "replaceSelectedText", text: message.text });
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return false if the message was not handled by this listener.
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function openChatGPT(promptText,action,curr_tabId){
|
||||||
|
return browser.windows.create({
|
||||||
|
url: "https://chat.openai.com",
|
||||||
|
type: "popup",
|
||||||
|
width: 700,
|
||||||
|
height: 800
|
||||||
|
}).then((newWindow) => {
|
||||||
|
console.log("Script started...");
|
||||||
|
createdWindowID = newWindow.id;
|
||||||
|
//console.log(promptText);
|
||||||
|
const tabId = newWindow.tabs[0].id;
|
||||||
|
browser.tabs.executeScript(tabId,{code: mzta_script})
|
||||||
|
.then(async () => {
|
||||||
|
console.log("Script injected successfully");
|
||||||
|
browser.tabs.sendMessage(tabId, {command: "chatgpt_send", prompt: promptText, action: action, tabId: curr_tabId});
|
||||||
|
}).catch(err => {
|
||||||
|
console.error("Error injecting the script: ", err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
27
options/mzta-options.css
Normal file
27
options/mzta-options.css
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
div#miczDescription{
|
||||||
|
padding:0px 15px 0px 15px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#miczDescription h1{
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#miczDonation{
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#miczPrefs {
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#miczRelNotes{
|
||||||
|
position:absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
padding: 2px 5px 0px 0px;
|
||||||
|
}
|
||||||
21
options/mzta-options.html
Normal file
21
options/mzta-options.html
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" href="mzta-options.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="miczRelNotes"><a href="mzta-release-notes.html">Release Notes</a></div>
|
||||||
|
<div id="miczPrefs"><label><input type="checkbox" id="force_msedge" name="force_msedge" class="option-input" /> __MSG_forceMsedgeOptionText__</label><br/>
|
||||||
|
<input type="checkbox" id="always_link" name="always_link" class="option-input" /> __MSG_AlwaysLinkOptionText__</label></div>
|
||||||
|
<div id="miczDescription">
|
||||||
|
<h1>__MSG_prefsInfoTitle__</h1>
|
||||||
|
<p>__MSG_prefsInfoDesc_1__<br/>
|
||||||
|
__MSG_prefsInfoDesc_2__<br/>
|
||||||
|
__MSG_prefsInfoDesc_3__</p>
|
||||||
|
</div>
|
||||||
|
<div id="miczDonation">__MSG_prefsDonation_1__<br/><a href="http://micz.it/thunderdbird-addon-thunderai/donate/">__MSG_prefsDonation_2__</a></div>
|
||||||
|
<script src="mzta-options.js"></script>
|
||||||
|
<script src="../js/mzta-i18n.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
31
options/mzta-options.js
Normal file
31
options/mzta-options.js
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
function saveOptions(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
let options = {};
|
||||||
|
document.querySelectorAll(".option-input").forEach(element => {
|
||||||
|
options[element.id] = element.checked;
|
||||||
|
});
|
||||||
|
browser.storage.sync.set(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreOptions() {
|
||||||
|
function setCurrentChoice(result) {
|
||||||
|
document.querySelectorAll(".option-input").forEach(element => {
|
||||||
|
element.checked = result[element.id] || false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
console.log(`Error: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let getting = browser.storage.sync.get(null);
|
||||||
|
getting.then(setCurrentChoice, onError);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
restoreOptions();
|
||||||
|
i18n.updateDocument();
|
||||||
|
document.querySelectorAll(".option-input").forEach(element => {
|
||||||
|
element.addEventListener("change", saveOptions);
|
||||||
|
});
|
||||||
|
}, { once: true });
|
||||||
19
options/mzta-release-notes.css
Normal file
19
options/mzta-release-notes.css
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
div#miczBackPrefs{
|
||||||
|
position:absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 0px;
|
||||||
|
padding: 2px 5px 0px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1{
|
||||||
|
font-size:16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2{
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul{
|
||||||
|
margin-top:0px;
|
||||||
|
}
|
||||||
15
options/mzta-release-notes.html
Normal file
15
options/mzta-release-notes.html
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="stylesheet" href="mzmo-release-notes.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="miczBackPrefs"><a href="mzta-options.html">__MSG_backToOptionsText__</a></div>
|
||||||
|
<h1>ThunderAI Release Notes</h1>
|
||||||
|
<h2>Version 1.0 - 02/12/2023</h2>
|
||||||
|
<ul><li>First release.</li></ul>
|
||||||
|
<script src="mzta-release-notes.js"></script>
|
||||||
|
<script src="../js/mzta-i18n.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
options/mzta-release-notes.js
Normal file
3
options/mzta-release-notes.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
i18n.updateDocument();
|
||||||
|
}, { once: true });
|
||||||
Loading…
Reference in a new issue