Merge pull request #54 from micz/custom_prompts

Custom Prompts
This commit is contained in:
Mic 2024-05-14 23:02:26 +02:00 committed by GitHub
commit 3a4fffdc93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 3487 additions and 86 deletions

View file

@ -1,6 +1,17 @@
# ![ThunderAI icon](images/icon-32px.png "ThunderAI") ThunderAI Release Notes
<h2>Version 1.1.0 - ??/??/2024</h2>
<ul>
<li>A prompt can be configured to request additional text from the user.</li>
<li>Added custom prompts. See more info <a href="https://micz.it/thunderdbird-addon-thunderai/custom-prompts/">here</a>.</li>
<li>Dark mode added.</li>
</ul>
<h2>Version 1.0.6 - 08/05/2024</h2>
<ul>
<li>Correctly handling a change in the ChatGPT web interface. See issue <a href="https://github.com/micz/ThunderAI/issues/43">#43</a></li>
<li>Added a button to force the completion to display the 'Use last answer' button if ChatGPT has finished its work but the button has not appeared.</li>
</ul>
<h2>Version 1.0.5 - 03/05/2024</h2>
<ul>
<li>Fixed the handling of ChatGPT-4 in a large window. See issue <a href="https://github.com/micz/ThunderAI/issues/38">#38</a></li>

View file

@ -54,6 +54,186 @@
"description": ""
},
"customPrompts_managePrompts": {
"message": "Manage Prompts",
"description": ""
},
"customPrompts_start_saving": {
"message": "Saving prompts...",
"description": ""
},
"customPrompts_reindexing_list": {
"message": "Reindexing list...",
"description": ""
},
"customPrompts_filtering_prompts": {
"message": "Filtering prompts...",
"description": ""
},
"customPrompts_saving_default_prompts": {
"message": "Saving default prompts...",
"description": ""
},
"customPrompts_saving_custom_prompts": {
"message": "Saving custom prompts...",
"description": ""
},
"customPrompts_reloading_menus": {
"message": "Reloading menus...",
"description": ""
},
"customPrompts_saved": {
"message": "Prompts saved!",
"description": ""
},
"customPrompts_managePrompts_statement": {
"message": "Manage your custom prompts.",
"description": ""
},
"customPrompts_form_label_ID": {
"message": "ID",
"description": ""
},
"customPrompts_form_label_ID_rules": {
"message": "Must be unique, lowercase and without spaces",
"description": ""
},
"customPrompts_form_label_Name": {
"message": "Name",
"description": ""
},
"customPrompts_form_label_Text": {
"message": "Prompt Text",
"description": ""
},
"customPrompts_form_label_Action": {
"message": "Action",
"description": ""
},
"customPrompts_form_label_need_selected": {
"message": "Text selection needed",
"description": ""
},
"customPrompts_form_label_need_signature": {
"message": "Always add the signature",
"description": ""
},
"customPrompts_form_label_need_custom_text": {
"message": "Ask for additional text",
"description": ""
},
"customPrompts_form_label_enabled": {
"message": "Enabled",
"description": ""
},
"customPrompts_form_required_fields": {
"message": "Required fields",
"description": ""
},
"customPrompts_btnEdit": {
"message": "Edit",
"description": ""
},
"customPrompts_btnCancel": {
"message": "Cancel",
"description": ""
},
"customPrompts_btnOK": {
"message": "OK",
"description": ""
},
"customPrompts_btnDelete": {
"message": "Delete",
"description": ""
},
"customPrompts_btnDelete_confirmText": {
"message": "Are you sure you want to delete this item?",
"description": ""
},
"customPrompts_unsaved_changes": {
"message": "There are unsaved changes!",
"description": ""
},
"customPrompts_btnSaveAll": {
"message": "Save All",
"description": ""
},
"customPrompts_save_button": {
"message": "Save",
"description": ""
},
"customPrompts_btnNew": {
"message": "Add New",
"description": ""
},
"customPrompts_btnAddNewCommit": {
"message": "Add the Prompt",
"description": ""
},
"customPrompts_add_to_menu": {
"message": "Add to menu",
"description": ""
},
"customPrompts_add_to_menu_always": {
"message": "Always",
"description": ""
},
"customPrompts_add_to_menu_reading": {
"message": "Reading an email",
"description": ""
},
"customPrompts_add_to_menu_composing": {
"message": "Composing an email",
"description": ""
},
"customPrompts_close_button": {
"message": "Close button",
"description": ""
},
"customPrompts_do_reply": {
"message": "Do reply",
"description": ""
},
"customPrompts_substitute_text": {
"message": "Substitute text",
"description": ""
},
"chatgpt_win_working": {
"message": "Work in progress...",
"description": ""
@ -99,6 +279,16 @@
"description": ""
},
"chatgpt_force_completion": {
"message": "force completion",
"description": ""
},
"chatgpt_force_completion_title": {
"message": "Click here to display the 'Use last answer' button if ChatGPT has finished its work but the button has not appeared.",
"description": ""
},
"msg_prompt_too_long": {
"message": "The text you provided is too long. You need to reduce it to use ChatGPT.",
"description": ""
@ -164,6 +354,11 @@
"description": ""
},
"prefs_OptionText_btnManagePrompts": {
"message": "Manage your prompts",
"description": ""
},
"prefsInfoTitle": {
"message": "Important Information",
"description": ""

View file

@ -1,201 +1,318 @@
{
"extensionDescription": {
"message": "Usa ChatGPT per migliorare le tue email!",
"description": "Descrizione dell'estensione."
"description": "Description of the extension."
},
"menu_title": {
"message": "IA",
"description": ""
},
"prompt_lang": {
"message": "Rispondi in ",
"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_this": {
"message": "Chedi a ChatGPT",
"description": ""
},
"prompt_selection_needed": {
"message": "Per procedere è necessario che selezioni del testo!",
"description": ""
},
"customPrompts_managePrompts": {
"message": "Gestisci i prompt",
"description": ""
},
"customPrompts_start_saving": {
"message": "Salvataggio prompt in corso...",
"description": ""
},
"customPrompts_reindexing_list": {
"message": "Indicizzazione lista...",
"description": ""
},
"customPrompts_filtering_prompts": {
"message": "Applicazione filtro in corso...",
"description": ""
},
"customPrompts_saving_default_prompts": {
"message": "Salvataggio prompt di base...",
"description": ""
},
"customPrompts_saving_custom_prompts": {
"message": "Salvataggio prompt personalizzati...",
"description": ""
},
"customPrompts_reloading_menus": {
"message": "Caricamento menu aggiornati...",
"description": ""
},
"customPrompts_saved": {
"message": "Prompt salvati!",
"description": ""
},
"customPrompts_managePrompts_statement": {
"message": "Gestisci i tuoi prompt personalizzati.",
"description": ""
},
"customPrompts_form_label_ID": {
"message": "ID",
"description": ""
},
"customPrompts_form_label_ID_rules": {
"message": "Deve essere univoco, minuscolo e senza spazi.",
"description": ""
},
"customPrompts_form_label_Name": {
"message": "Nome",
"description": ""
},
"customPrompts_form_label_Text": {
"message": "Testo del prompt",
"description": ""
},
"customPrompts_form_label_Action": {
"message": "Azione",
"description": ""
},
"customPrompts_form_label_need_selected": {
"message": "Necessaria la selezione di testo",
"description": ""
},
"customPrompts_form_label_need_signature": {
"message": "Aggiungi sempre la firma",
"description": ""
},
"customPrompts_form_label_need_custom_text": {
"message": "Chiedi un testo aggiuntivo",
"description": ""
},
"customPrompts_form_label_enabled": {
"message": "Attivo",
"description": ""
},
"customPrompts_form_required_fields": {
"message": "Campi obbligatori",
"description": ""
},
"customPrompts_btnEdit": {
"message": "Modifica",
"description": ""
},
"customPrompts_btnCancel": {
"message": "Annulla",
"description": ""
},
"customPrompts_btnOK": {
"message": "OK",
"description": ""
},
"customPrompts_btnDelete": {
"message": "Cancella",
"description": ""
},
"customPrompts_btnDelete_confirmText": {
"message": "Sei sicuro di voler cancellare questo elemento?",
"description": ""
},
"customPrompts_unsaved_changes": {
"message": "Ci sono modifiche non salvate!",
"description": ""
},
"customPrompts_btnSaveAll": {
"message": "Salva Tutto",
"description": ""
},
"customPrompts_save_button": {
"message": "Salva",
"description": ""
},
"customPrompts_btnNew": {
"message": "Aggiungi Nuovo",
"description": ""
},
"customPrompts_btnAddNewCommit": {
"message": "Aggiungi il Prompt",
"description": ""
},
"customPrompts_add_to_menu": {
"message": "Aggiungi al menu",
"description": ""
},
"customPrompts_add_to_menu_always": {
"message": "Sempre",
"description": ""
},
"customPrompts_add_to_menu_reading": {
"message": "In lettura di una mail",
"description": ""
},
"customPrompts_add_to_menu_composing": {
"message": "In composizione di una mail",
"description": ""
},
"customPrompts_close_button": {
"message": "Pulsante chiudi",
"description": ""
},
"customPrompts_do_reply": {
"message": "Rispondi",
"description": ""
},
"customPrompts_substitute_text": {
"message": "Sostituisci il 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": ""
},
"chatgpt_user_not_logged_in": {
"message": "Non sei connesso a ChatGPT. Effettua l'accesso con le tue credenziali, chiudi la finestra di ChatGPT e poi ripeti l'azione che avevi tentato. Rimarrai connesso in seguito.",
"description": ""
},
"chatgpt_win_model_warning": {
"message": "Hai impostato l'opzione per utilizzare ChatGPT-4, ma sembra che non si stia caricando. Hai selezionato correttamente il modello in alto?",
"description": ""
},
"chatgpt_win_custom_text": {
"message": "Inserisci qui il testo aggiuntivo per il prompt.",
"description": ""
},
"chatgpt_win_send": {
"message": "Invia",
"description": ""
},
"chatgpt_use_gpt35": {
"message": "Usa GPT3.5",
"description": ""
},
"chatgpt_force_completion": {
"message": "forza il completamento",
"description": ""
},
"chatgpt_force_completion_title": {
"message": "Fai clic qui per visualizzare il pulsante 'Usa l'ultima risposta' se ChatGPT ha terminato il suo lavoro ma il pulsante non è apparso.",
"description": ""
},
"msg_prompt_too_long": {
"message": "Il testo che vuoi inviare a ChatGPT è troppo lungo. Devi ridurlo per poter procedere.",
"description": ""
},
"prefs_OptionText_release_notes": {
"message": "Note di rilascio",
"description": ""
},
"prefs_status_page": {
"message": "stato del servizio",
"description": ""
},
"prefs_OptionText_chatgpt_win_text": {
"message": "Dimensioni della finestra di ChatGPT",
"description": ""
},
"prefs_OptionText_chatgpt_win_height": {
"message": "Altezza",
"description": ""
},
"prefs_OptionText_chatgpt_win_width": {
"message": "Larghezza",
"description": ""
},
"prefs_OptionText_default_sign_name": {
"message": "Nome da utilizzare per la firma",
"description": ""
},
"prefs_OptionText_default_chatgpt_lang": {
"message": "Lingua predefinita per le risposte di ChatGPT",
"description": ""
},
"prefs_OptionText_reply_all": {
"message": "Rispondi a tutti",
"description": ""
},
"prefs_OptionText_reply_sender": {
"message": "Rispondi al mittente",
"description": ""
},
"prefs_OptionText_reply_type": {
"message": "Modalità di risposta",
"description": ""
},
"prefs_OptionText_chatpgt_use_gpt4": {
"message": "Usa il modello ChatGPT-4",
"description": ""
},
"prefs_OptionText_chatpgt_use_gpt4_info": {
"message": "Dopo aver modificato questa opzione, devi scegliere il modello corretto la prima volta che lo usi.",
"description": ""
},
"prefs_OptionText_btnManagePrompts": {
"message": "Gestisci i tuoi prompts",
"description": ""
},
"prefsInfoTitle": {
"message": "Informazioni Importanti",
"description": ""
},
"prefsInfoDesc_1": {
"message": "Non è più possibile disabilitare la cronologia delle chat di ChatGPT. Pertanto, non c'è modo di impedire che le richieste effettuate tramite questo componente aggiuntivo appaiano nella cronologia delle chat di ChatGPT.",
"description": ""
},
"prefsInfoDesc_2": {
"message": "Se all'apertura della finestra di ChatGPT, viene mostrata una pagina bianca, significa che c'è un problema di caricamento della pagina di ChatGPT, non relativo all'utilizzo di questo componente aggiuntivo.",
"description": ""
},
"prefsInfoDesc_3": {
"message": " ",
"description": ""
},
"prefsDonation_1": {
"message": "Ti piace questo componente aggiuntivo?",
"description": ""
},
"prefsDonation_2": {
"message": "Considera di fare una donazione!",
"description": ""
},
"backToOptionsText": {
"message": "Opzioni",
"description": ""
}
}
}

2020
customprompts/list.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,172 @@
@media (prefers-color-scheme: light) {
table th {
background-color: #ffffff;
}
#command_palette{
background-color: #ffffff;
}
table.prompts_list tr:nth-child(even) {
background-color: #e9e9e9;
}
table.prompts_list tr:nth-child(odd) {
background-color: #f3f3f3;
}
}
@media (prefers-color-scheme: dark) {
body {
background-color: #1C1B22;
color: rgb(251, 251, 254);
}
a:link { color: #409EFF; }
a:visited { color: #409EFF; }
a:hover { color: #66B1FF; }
a:active { color: #66B1FF; }
table {
background-color: #1C1B22;
border: 1px solid #ccc;
}
table.prompts_list th {
background-color: #4c4e58;
border: 1px solid rgb(251, 251, 254);
color: rgb(194, 194, 194);
}
#command_palette{
background-color: inherit;
}
table.prompts_list tr:nth-child(even) {
background-color: #2E2F36;
}
}
table {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #ccc;
}
thead {
position: sticky;
top: 26px;
}
#command_palette{
position: sticky;
top: 0;
padding-top: 1px;
height: 26px;
width: 100%;
}
#btnNew{
float: right;
}
#btnSaveAll{
float: left;
}
#msgDisplay{
padding-left: 40px;
}
table tr {
border-bottom: 1px solid #ccc;
}
table.prompts_list th {
padding: 8px;
text-align: left;
}
table td {
border: 1px solid #ddd;
padding: 8px;
}
table td.w08{
width: 8vw;
}
table td.w17{
width: 17vw;
}
table td.w19{
width: 19vw;
}
table td.w25{
width: 25vw;
}
table td.w40{
width: 40vw;
}
table.prompts_list td.properties{
white-space: nowrap;
}
table .sort {
background-image: url(../images/listjs-arrow-sort-inactive.png);
background-repeat: no-repeat;
background-position: right center;
padding-right: 14px;
cursor: pointer;
cursor: hand;
}
table .sort.asc {
background-image: url(../images/listjs-arrow-sort-active-down.png);
}
table .sort.desc {
background-image: url(../images/listjs-arrow-sort-active-up.png);
}
.hiddendata{
display: none;
}
#formNew{
display: none;
width: 100%;
padding-bottom: 7px;
}
.text_output{
width: 98%;
height: 9em;
}
.go_right_float{
float: right;
}
.go_right{
width: 100%;
text-align: right;
}
.width_all{
width: 100%;
}
.small_label{
font-size: small;
font-style: italic;
}
#txtIdNew, .id_output{
text-transform: lowercase;
}

View file

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ThunderAI - __MSG_customPrompts_managePrompts__</title>
<link rel="stylesheet" href="mzta-custom-prompts.css">
</head>
<body>
<div>
<h1>__MSG_customPrompts_managePrompts__</h1>
<p>__MSG_customPrompts_managePrompts_statement__</p>
</div>
<div id="command_palette">
<button id="btnSaveAll" disabled>__MSG_customPrompts_btnSaveAll__</button>
<span id="msgDisplay"></span>
<button id="btnNew">__MSG_customPrompts_btnNew__</button>
</div>
<div id="formNew">
<table class="width_all">
<tr>
<td class="w19">
<label for="txtIdNew">*__MSG_customPrompts_form_label_ID__:</label>
<br>
<input type="text" id="txtIdNew" name="id" class="input_new">
<br><span class="small_label">[__MSG_customPrompts_form_label_ID_rules__]</span>
</td>
<td rowspan="2" class="w25">
<label for="txtTextNew">*__MSG_customPrompts_form_label_Text__:</label>
<br>
<textarea id="txtTextNew" name="text" class="input_new text_output"></textarea>
</td>
<td class="w19">
<label for="selectTypeNew">__MSG_customPrompts_add_to_menu__:</label>
<br>
<select id="selectTypeNew" name="type">
<option value="0">__MSG_customPrompts_add_to_menu_always__</option>
<option value="1">__MSG_customPrompts_add_to_menu_reading__</option>
<option value="2">__MSG_customPrompts_add_to_menu_composing__</option>
</select>
</td>
<td rowspan="2" class="w19">
<input type="checkbox" id="checkboxNeedSelectedNew" name="need_selected" value="1"> <label for="checkboxNeedSelectedNew">__MSG_customPrompts_form_label_need_selected__</label>
<br>
<input type="checkbox" id="checkboxNeedSignatureNew" name="need_signature" value="1"> <label for="checkboxNeedSignatureNew">__MSG_customPrompts_form_label_need_signature__</label>
<br>
<input type="checkbox" id="checkboxNeedCustomTextNew" name="need_custom_text" value="1"> <label for="checkboxNeedCustomTextNew">__MSG_customPrompts_form_label_need_custom_text__</label>
</td>
</tr>
<tr>
<td class="w19">
<label for="txtNameNew">*__MSG_customPrompts_form_label_Name__:</label>
<br>
<input type="text" id="txtNameNew" name="name" class="input_new">
</td>
<td class="w19">
<label for="selectActionNew">__MSG_customPrompts_form_label_Action__:</label>
<br>
<select id="selectActionNew" name="action">
<option value="0">__MSG_customPrompts_close_button__</option>
<option value="1">__MSG_customPrompts_do_reply__</option>
<option value="2">__MSG_customPrompts_substitute_text__</option>
</select>
</td>
</tr>
</table>
<div class="go_right_float">* __MSG_customPrompts_form_required_fields__</div>
<br>
<div class="go_right"><button id="btnAddNew" disabled>__MSG_customPrompts_btnAddNewCommit__</button></div>
</div>
<div id="all_prompts">
<table class="prompts_list">
<thead>
<tr>
<th class="sort" data-sort="id">__MSG_customPrompts_form_label_ID__</th>
<th class="sort" data-sort="name">__MSG_customPrompts_form_label_Name__</th>
<th class="sort" data-sort="text">__MSG_customPrompts_form_label_Text__</th>
<th class="sort" data-sort="type">__MSG_customPrompts_add_to_menu__</th>
<th>Properties</th>
<th>__MSG_customPrompts_form_label_Action__</th>
</tr>
</thead>
<tbody class="list">
</tbody>
</table>
</div>
<script src="list.js"></script>
<script src="mzta-custom-prompts.js" type="module"></script>
<script src="../js/mzta-i18n.js"></script>
</body>
</html>

View file

@ -0,0 +1,525 @@
/*
* ThunderAI [https://micz.it/thunderdbird-addon-thunderai/]
* Copyright (C) 2024 Mic (m@micz.it)
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getPrompts, setDefaultPromptsProperties, setCustomPrompts } from "../js/mzta-prompts.js";
var promptsList = null;
var somethingChanged = false;
var positionMax_compose = 0;
var positionMax_display = 0;
var idnumMax = 0;
var msgTimeout = null;
document.addEventListener('DOMContentLoaded', async () => {
let options = {
valueNames: [ { data: ['idnum'] }, 'is_default', 'id', 'name', 'text', 'type', 'action', 'position_compose', 'position_display', { name: 'need_selected', attr: 'checked_val'}, { name: 'need_signature', attr: 'checked_val'}, { name: 'need_custom_text', attr: 'checked_val'}, { name: 'enabled', attr: 'checked_val'} ],
item: function(values) {
let type_output = '';
switch(String(values.type)){
case "0":
type_output = `__MSG_customPrompts_add_to_menu_always__`;
break;
case "1":
type_output = `__MSG_customPrompts_add_to_menu_reading__`;
break;
case "2":
type_output = `__MSG_customPrompts_add_to_menu_composing__`;
break;
}
let action_output = '';
switch(String(values.action)){
case "0":
action_output = `__MSG_customPrompts_close_button__`;
break;
case "1":
action_output = `__MSG_customPrompts_do_reply__`;
break;
case "2":
action_output = `__MSG_customPrompts_substitute_text__`;
break;
}
//console.log('>>>>>>>>>>>>> action_output: ' + JSON.stringify(action_output));
let output = `<tr ` + ((values.is_default == 1) ? 'class="is_default"':'') + `>
<td class="w08"><span class="id id_show"></span><input type="text" class="hiddendata id_output" value="` + values.id + `" /></td>
<td class="w08"><span class="name name_show"></span><input type="text" class="hiddendata name_output" value="` + values.name + `" /></td>
<td class="w40"><span class="text text_show"></span><textarea class="hiddendata text_output">` + values.text + `</textarea></td>
<td><span class="type_show">` + type_output + `</span>
<select class="type_output hiddendata">
<option value="0"` + ((values.type == "0") ? ' selected':'') + `>__MSG_customPrompts_add_to_menu_always__</option>
<option value="1"` + ((values.type == "1") ? ' selected':'') + `>__MSG_customPrompts_add_to_menu_reading__</option>
<option value="2"` + ((values.type == "2") ? ' selected':'') + `>__MSG_customPrompts_add_to_menu_composing__</option>
</select>` +
`<span class="type hiddendata"></span>
</td>
<td class="w17">
Action: <span class="action_show">` + action_output + `</span>
<select class="action_output hiddendata">
<option value="0"` + ((values.action == "0") ? ' selected':'') + `>__MSG_customPrompts_close_button__</option>
<option value="1"` + ((values.action == "1") ? ' selected':'') + `>__MSG_customPrompts_do_reply__</option>
<option value="2"` + ((values.action == "2") ? ' selected':'') + `>__MSG_customPrompts_substitute_text__</option>
</select>` +
`<span class="action hiddendata"></span>
<br>
<input type="checkbox" class="need_selected" disabled> __MSG_customPrompts_form_label_need_selected__
<br>
<input type="checkbox" class="need_signature" disabled> __MSG_customPrompts_form_label_need_signature__
<br>
<input type="checkbox" class="need_custom_text" disabled> __MSG_customPrompts_form_label_need_custom_text__
<br>
<input type="checkbox" class="enabled input_mod"> __MSG_customPrompts_form_label_enabled__
<span class="is_default hiddendata"></span>
<span class="position_compose hiddendata"></span>
<span class="position_display hiddendata"></span>
</td>
<td>
<button class="btnEditItem"` + ((values.is_default == 1) ? ' disabled':'') + `>__MSG_customPrompts_btnEdit__</button>
<button class="btnCancelItem hiddendata"` + ((values.is_default == 1) ? ' disabled':'') + `>__MSG_customPrompts_btnCancel__</button>
<br><br>
<button class="btnConfirmItem hiddendata"` + ((values.is_default == 1) ? ' disabled':'') + `>__MSG_customPrompts_btnOK__</button>
<button class="btnDeleteItem"` + ((values.is_default == 1) ? ' disabled':'') + `>__MSG_customPrompts_btnDelete__</button>
</td>
</tr>`;
//console.log('>>>>>>>> values.name: ' + JSON.stringify(values.name));
positionMax_compose = Math.max(positionMax_compose, values.position_compose);
positionMax_display = Math.max(positionMax_display, values.position_display);
idnumMax = Math.max(idnumMax, values.idnum);
return output;
}
};
let values = await getPrompts();
console.log('>>>>>>>>>>>>>>>> values: ' + JSON.stringify(values));
promptsList = new List('all_prompts', options, values);
checkSelectedBoxes();
const btnSaveAll = document.getElementById('btnSaveAll');
btnSaveAll.disabled = true;
// Disable save button and handle save actions
function handleSaveAllClick(e) {
e.preventDefault();
saveAll();
clearFields();
}
btnSaveAll.addEventListener('click', handleSaveAllClick);
const btnNew = document.getElementById('btnNew');
// Show the new item form
function handleNewClick(e) {
e.preventDefault();
e.target.disabled = true;
document.getElementById('formNew').style.display = 'block';
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}
btnNew.addEventListener('click', handleNewClick);
// Enable save button on input change
function handleInputChange(e) {
e.preventDefault();
setSomethingChanged();
}
document.querySelectorAll('.input_mod').forEach(element => {
element.addEventListener('change', handleInputChange);
});
// Display selected value next to select input
// function handleSelectChange(e) {
// e.preventDefault();
// const spanElement = e.target.nextElementSibling;
// spanElement.textContent = e.target.value;
// }
// document.querySelectorAll('select.input_mod').forEach(element => {
// element.addEventListener('change', handleSelectChange);
// });
// Log data ID number from item row and prepare for edit action
function handleEditClick(e) {
e.preventDefault();
const tr = e.target.parentNode.parentNode;
//console.log('>>>>>>>> tr: ' + tr.getAttribute('data-idnum'));
e.target.style.display = 'none'; // Edit btn
tr.querySelector('.btnConfirmItem').style.display = 'inline'; // Save btn
tr.querySelector('.btnCancelItem').style.display = 'inline'; // Cancel btn
// tr.querySelector('.btnEditItem').style.display = 'none'; // Edit btn
tr.querySelector('.btnDeleteItem').style.display = 'none'; // Delete btn
showItemRowEditor(tr);
}
let btnEditItem_elements = document.querySelectorAll(".btnEditItem");
btnEditItem_elements.forEach(element => {
element.addEventListener('click', handleEditClick);
});
function showItemRowEditor(tr) {
tr.querySelector('.id_output').style.display = 'inline';
tr.querySelector('.id_show').style.display = 'none';
tr.querySelector('.name_output').style.display = 'inline';
tr.querySelector('.name_show').style.display = 'none';
tr.querySelector('.text_output').style.display = 'inline';
tr.querySelector('.text_show').style.display = 'none';
tr.querySelector('.type_output').style.display = 'inline';
tr.querySelector('.type_show').style.display = 'none';
tr.querySelector('.action_output').style.display = 'inline';
tr.querySelector('.action_show').style.display = 'none';
tr.querySelector('input.need_selected').disabled = false;
tr.querySelector('input.need_signature').disabled = false;
tr.querySelector('input.need_custom_text').disabled = false;
}
function hideItemRowEditor(tr) {
tr.querySelector('.id_output').style.display = 'none';
tr.querySelector('.id_show').style.display = 'inline';
tr.querySelector('.name_output').style.display = 'none';
tr.querySelector('.name_show').style.display = 'inline';
tr.querySelector('.text_output').style.display = 'none';
tr.querySelector('.text_show').style.display = 'inline';
tr.querySelector('.type_output').style.display = 'none';
tr.querySelector('.type_show').style.display = 'inline';
tr.querySelector('.action_output').style.display = 'none';
tr.querySelector('.action_show').style.display = 'inline';
tr.querySelector('input.need_selected').disabled = true;
tr.querySelector('input.need_signature').disabled = true;
tr.querySelector('input.need_custom_text').disabled = true;
}
// Confirm and log deletion action
function handleDeleteClick(e) {
e.preventDefault();
const checkConfirm = window.confirm(browser.i18n.getMessage("customPrompts_btnDelete_confirmText"));
if (!checkConfirm) {
return;
}
const tr = e.target.parentNode.parentNode;
//console.log('>>>>>>>> tr: ' + tr.getAttribute('data-idnum'));
promptsList.remove("id", tr.querySelector('span.id').innerText);
setSomethingChanged();
}
let btnDeleteItem_elements = document.querySelectorAll(".btnDeleteItem");
btnDeleteItem_elements.forEach(element => {
element.addEventListener('click', handleDeleteClick);
});
function handleCancelClick(e) {
e.preventDefault();
const tr = e.target.parentNode.parentNode;
e.target.style.display = 'none'; // Cancel btn
tr.querySelector('.btnConfirmItem').style.display = 'none'; // Save btn
// tr.querySelector('.btnCancelItem').style.display = 'none'; // Cancel btn
tr.querySelector('.btnEditItem').style.display = 'inline'; // Edit btn
tr.querySelector('.btnDeleteItem').style.display = 'inline'; // Delete btn
hideItemRowEditor(tr);
}
let btnCancelItem_elements = document.querySelectorAll(".btnCancelItem");
btnCancelItem_elements.forEach(element => {
element.addEventListener('click', handleCancelClick);
});
function handleConfirmClick(e) {
e.preventDefault();
const tr = e.target.parentNode.parentNode;
e.target.style.display = 'none'; // Ok btn
// tr.querySelector('.btnConfirmItem').style.display = 'none'; // Ok btn
tr.querySelector('.btnCancelItem').style.display = 'none'; // Cancel btn
tr.querySelector('.btnEditItem').style.display = 'inline'; // Edit btn
tr.querySelector('.btnDeleteItem').style.display = 'inline'; // Delete btn
// Update item data
tr.querySelector('.id_show').innerText = String(tr.querySelector('.id_output').value).toLocaleLowerCase();
tr.querySelector('.name_show').innerText = tr.querySelector('.name_output').value;
tr.querySelector('.text_show').innerText = tr.querySelector('.text_output').value;
tr.querySelector('.type').innerText = tr.querySelector('.type_output').value;
tr.querySelector('.type_show').innerText = tr.querySelector('.type_output').selectedOptions[0].text;
tr.querySelector('.action').innerText = tr.querySelector('.action_output').value;
tr.querySelector('.action_show').innerText = tr.querySelector('.action_output').selectedOptions[0].text;
// the checkboxes update is handled directly by themselves
hideItemRowEditor(tr);
setSomethingChanged();
}
let btnConfirmItem_elements = document.querySelectorAll(".btnConfirmItem");
btnConfirmItem_elements.forEach(element => {
element.addEventListener('click', handleConfirmClick);
});
// Handle checkbox changes and log new state
function handleCheckboxChange(e) {
e.preventDefault();
e.target.setAttribute('checked_val', e.target.checked ? '1' : '0');
//console.log('>>>>>>>> checked_val: ' + e.target.getAttribute('checked_val'));
}
let checkbox_elements = document.querySelectorAll("input[type='checkbox']");
checkbox_elements.forEach(element => {
element.addEventListener('change', handleCheckboxChange);
});
// Handle "type" select changes and log new state
// function handleTypeSelectChange(e) {
// e.preventDefault();
// const spanElement = e.target.nextElementSibling;
// spanElement.textContent = e.target.value;
// }
// let type_select_elements = document.querySelectorAll("select.type_output");
// type_select_elements.forEach(element => {
// element.addEventListener('change', handleTypeSelectChange);
// });
// Handle "action" select changes and log new state
// function handleActionSelectChange(e) {
// e.preventDefault();
// const spanElement = e.target.nextElementSibling;
// spanElement.textContent = e.target.value;
// }
// let action_select_elements = document.querySelectorAll("select.action_output");
// action_select_elements.forEach(element => {
// element.addEventListener('change', handleActionSelectChange);
// });
// for the new prompt form
let btnNew_elements = document.querySelectorAll(".input_new");
if(btnNew_elements) {
btnNew_elements.forEach(element => {
element.addEventListener('change', (e) => {
e.preventDefault();
checkFields();
});
});
}
i18n.updateDocument();
//To add a new item
var txtIdNew = document.getElementById('txtIdNew');
var txtNameNew = document.getElementById('txtNameNew');
var txtTextNew = document.getElementById('txtTextNew');
var selectTypeNew = document.getElementById('selectTypeNew');
var selectActionNew = document.getElementById('selectActionNew');
var checkboxNeedSelectedNew = document.getElementById('checkboxNeedSelectedNew');
var checkboxNeedSignatureNew = document.getElementById('checkboxNeedSignatureNew');
var checkboxNeedCustomTextNew = document.getElementById('checkboxNeedCustomTextNew');
const btnAddNew = document.getElementById('btnAddNew');
btnAddNew.addEventListener('click', (e) => {
e.preventDefault();
if(!checkFields()) {
return;
}
let newItem = promptsList.add({
id: String(txtIdNew.value.trim()).toLocaleLowerCase(),
name: txtNameNew.value.trim(),
text: txtTextNew.value.trim(),
type: selectTypeNew.value,
action: selectActionNew.value,
need_selected: (checkboxNeedSelectedNew.checked) ? 1 : 0,
need_signature: (checkboxNeedSignatureNew.checked) ? 1 : 0,
need_custom_text: (checkboxNeedCustomTextNew.checked) ? 1 : 0,
enabled: 1,
position_compose: positionMax_compose + 1,
position_display: positionMax_display + 1,
is_default: 0,
idnum: idnumMax + 1,
});
idnumMax++;
let curr_idnum = newItem[0].values().idnum;
let checkboxes = document.querySelectorAll(`tr[data-idnum="${curr_idnum}"] input[type="checkbox"]`);
checkSelectedBoxes(checkboxes);
let editBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnEditItem`);
//console.log(`>>>>>>>>>>>> tr[data-idnum="${curr_idnum}"] button.btnEditItem`);
editBtn.addEventListener('click', handleEditClick);
//console.log('>>>>>>>>>>>>> editBtn: ' + JSON.stringify(editBtn));
let deleteBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnDeleteItem`);
//console.log(`>>>>>>>>>>>> tr[data-idnum="${curr_idnum}"] button.btnDeleteItem`);
deleteBtn.addEventListener('click', handleDeleteClick);
let okBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnConfirmItem`);
okBtn.addEventListener('click', handleConfirmClick);
let cancelBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnCancelItem`);
cancelBtn.addEventListener('click', handleCancelClick);
// console.log('>>>>>>>>>>>>> deleteBtn: ' + JSON.stringify(deleteBtn));
// console.log('>>>>>>>>>>>>> newItem: ' + JSON.stringify(newItem));
document.getElementById('btnNew').disabled = false;
clearFields();
setSomethingChanged();
i18n.updateDocument();
window.scrollTo({
top: document.body.scrollHeight,
behavior: 'smooth'
});
});
}, { once: true });
function checkFields() {
//console.log('>>>>>>>>>>>>> typeof promptsList: ' + typeof promptsList);
//console.log('>>>>>>>>>>>>> Array.isArray(promptsList): ' + Array.isArray(promptsList));
// the id must be unique and without spaces
let is_error = false;
let id_value = document.getElementById('txtIdNew').value.trim();
if ((id_value == '')||
(/\s/.test(id_value))) {
inputSetError('txtIdNew');
is_error = true;
} else {
let exists = promptsList.get("id", id_value);
//console.log('>>>>>>>>>>>>> exists: ' + JSON.stringify(exists));
if(exists && exists.length > 0) {
inputSetError('txtIdNew');
is_error = true;
} else {
inputClearError('txtIdNew');
}
}
if (document.getElementById('txtNameNew').value.trim() == '') {
inputSetError('txtNameNew');
is_error = true;
} else {
inputClearError('txtNameNew');
}
if (document.getElementById('txtTextNew').value.trim() == '') {
inputSetError('txtTextNew');
is_error = true;
} else {
inputClearError('txtTextNew');
}
document.getElementById('btnAddNew').disabled = is_error;
return !is_error;
}
function clearFields() {
document.getElementById('txtIdNew').value = '';
document.getElementById('txtNameNew').value = '';
document.getElementById('txtTextNew').value = '';
document.getElementById('selectTypeNew').value = '0';
document.getElementById('selectActionNew').value = '0';
document.getElementById('checkboxNeedSelectedNew').value = '0';
document.getElementById('checkboxNeedSignatureNew').value = '0';
document.getElementById('checkboxNeedCustomTextNew').value = '0';
document.getElementById('formNew').style.display = 'none';
}
function inputSetError(input) {
document.getElementById(input).style.borderColor = 'red';
}
function inputClearError(input) {
document.getElementById(input).style.borderColor = 'green';
}
function setSomethingChanged(){
clearTimeout(msgTimeout);
somethingChanged = true;
document.getElementById('btnSaveAll').disabled = false;
let msgDisplay = document.getElementById('msgDisplay');
msgDisplay.innerHTML = browser.i18n.getMessage('customPrompts_unsaved_changes');
msgDisplay.style.display = 'inline';
msgDisplay.style.color = 'red';
}
function setNothingChanged(){
somethingChanged = false;
document.getElementById('btnSaveAll').disabled = true;
let msgDisplay = document.getElementById('msgDisplay');
msgDisplay.disabled = true;
msgDisplay.innerHTML = ''
msgDisplay.style.display = 'none';
msgDisplay.style.color = '';
}
function checkSelectedBoxes(checkboxes = null) {
if(checkboxes == null){
checkboxes = [
...document.querySelectorAll('.need_selected[type="checkbox"]'),
...document.querySelectorAll('.need_signature[type="checkbox"]'),
...document.querySelectorAll('.need_custom_text[type="checkbox"]'),
...document.querySelectorAll('.enabled[type="checkbox"]'),
];
}
// Iterate through the checkboxes
checkboxes.forEach(checkbox => {
// Check if the 'checked' attribute is "0"
if (checkbox.getAttribute('checked_val') == "0") {
// Uncheck the checkbox
checkbox.checked = false;
} else {
checkbox.checked = true;
}
});
}
//Save all prompts
async function saveAll() {
setMessage(browser.i18n.getMessage('customPrompts_start_saving'));
setNothingChanged();
if(promptsList != null) {
setMessage(browser.i18n.getMessage('customPrompts_reindexing_list'));
promptsList.reIndex();
let newPrompts = promptsList.items.map(item => {
// For each item in the array, return only the '_values' part
return item.values();
});
// newPrompts.forEach(prompt => {
// console.log('>>>>>>>>>>>>> id: ' + JSON.stringify(prompt));
// });
//console.log('>>>>>>>>>>>>> saveAll: ' + JSON.stringify(newPrompts));
setMessage(browser.i18n.getMessage('customPrompts_filtering_prompts'));
let newDefaultPrompts = newPrompts.filter(item => item.is_default == 1);
//console.log('>>>>>>>>>>>>> newDefaultPrompts: ' + JSON.stringify(newDefaultPrompts));
let newCustomPrompts = newPrompts.filter(item => item.is_default == 0);
setMessage(browser.i18n.getMessage('customPrompts_saving_default_prompts'));
await setDefaultPromptsProperties(newDefaultPrompts);
setMessage(browser.i18n.getMessage('customPrompts_saving_custom_prompts'));
await setCustomPrompts(newCustomPrompts);
setMessage(browser.i18n.getMessage('customPrompts_reloading_menus'));
browser.runtime.sendMessage({command: "reload_menus"});
setMessage(browser.i18n.getMessage('customPrompts_saved'),'green');
msgTimeout = setTimeout(() => {
clearMessage();
}, 10000)
}
}
function setMessage(msg, color = '') {
clearTimeout(msgTimeout);
let msgDisplay = document.getElementById('msgDisplay');
msgDisplay.innerHTML = msg;
msgDisplay.style.display = 'inline';
msgDisplay.style.color = color;
}
function clearMessage() {
let msgDisplay = document.getElementById('msgDisplay');
msgDisplay.innerHTML = '';
msgDisplay.style.display = 'none';
msgDisplay.style.color = '';
}
// window.addEventListener('beforeunload', function (event) {
// // Check if any changes have been made
// if (somethingChanged) {
// event.preventDefault();
// }
// });

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

View file

@ -47,7 +47,7 @@ async function chatgpt_isIdle() {
function chatgpt_getRegenerateButton() {
for (const mainSVG of document.querySelectorAll('main svg')) {
if (mainSVG.querySelector('path[d*="M4.5 2.5C5.05228"]')) // regen icon found
if (mainSVG.querySelector('path[d*="M3.07 10.876C3.623"]')) // regen icon found
return mainSVG.parentNode.parentNode;
}
}
@ -120,7 +120,8 @@ function addCustomDiv(prompt_action,tabId) {
style.innerHTML += "#mzta-model_warn{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);max-height:100%px;min-width:30%;max-width:50%;padding:3px;border-radius:5px;text-align:center;background-color:#FFBABA;border:1px solid;font-size:13px;color:#D8000C;display:none;}";
style.innerHTML += "#mzta-btn_gpt35 {background-color: #007bff;border: none;color: white;padding: 2px 4px;text-align: center;text-decoration: none;display: none;font-size: 13px;margin-left: 4px;transition-duration: 0.4s;cursor: pointer;border-radius: 2px;}";
style.innerHTML += "#mzta-status-page{position:fixed;bottom:0;left:0;padding-left:5px;font-size:13px;font-style:italic;text-decoration:underline;color:#919191;}";
style.innerHTML += "#mzta-status-page:hover{color:#007bff;}";
style.innerHTML += "#mzta-force-completion{cursor:pointer;position:fixed;bottom:0;right:0;padding-right:5px;font-size:13px;font-style:italic;text-decoration:underline;color:#919191;}";
style.innerHTML += "#mzta-status-page:hover, #mzta-force-completion:hover{color:#007bff;}";
style.innerHTML += "#mzta-custom_text{padding:10px;width:auto;max-width:80%;height:auto;max-height:80%;border-radius:5px;overflow:auto;position:fixed;top:50%;left:50%;display:none;transform:translate(-50%,-50%);text-align:center;background:#333;color:white;border:3px solid white;}";
style.innerHTML += "#mzta-custom_loading{height:50px;display:none;}";
style.innerHTML += "#mzta-custom_textarea{color:black;padding:1px;font-size:15px;width:100%;}";
@ -155,6 +156,16 @@ function addCustomDiv(prompt_action,tabId) {
status_page_div.innerHTML = '<a href="https://micz.it/thunderdbird-addon-thunderai/status/">'+ mztaStatusPageDesc +'</a>';
fixedDiv.appendChild(status_page_div);
//force completion
var force_completion_div = document.createElement('div');
force_completion_div.id = 'mzta-force-completion';
force_completion_div.innerHTML = mztaForceCompletionDesc;
force_completion_div.title = mztaForceCompletionTitle;
force_completion_div.addEventListener('click', function() {
operation_done();
});
fixedDiv.appendChild(force_completion_div);
// span for the text
var curr_msg = document.createElement('span');
curr_msg.id='mzta-curr_msg';
@ -275,6 +286,7 @@ function operation_done(){
document.getElementById('mzta-curr_msg').innerHTML = browser.i18n.getMessage("chatgpt_win_job_completed")+"<br>";
document.getElementById('mzta-btn_ok').style.display = 'inline';
document.getElementById('mzta-loading').style.display = 'none';
document.getElementById('mzta-force-completion').style.display = 'none';
chatpgt_scrollToBottom();
}

View file

@ -18,14 +18,15 @@
// Some original methods are derived from https://github.com/ali-raheem/Aify/blob/cfadf52f576b7be3720b5b73af7c8d3129c054da/plugin/html/actions.js
import { defaultPrompts } from './mzta-prompts.js';
import { getLanguageDisplayName } from './mzta-utils.js'
import { getPrompts } from './mzta-prompts.js';
import { getLanguageDisplayName, getMenuContextCompose, getMenuContextDisplay } from './mzta-utils.js'
export class mzta_Menus {
allPrompts = [];
openChatGPT = null;
menu_context_compose = 'compose_action_menu';
menu_context_display = 'message_display_action_menu';
menu_context_compose = null;
menu_context_display = null;
menu_listeners = {};
rootMenu = [
@ -33,16 +34,35 @@ export class mzta_Menus {
];
constructor(openChatGPT) {
this.menu_context_compose = getMenuContextCompose();
this.menu_context_display = getMenuContextDisplay();
this.openChatGPT = openChatGPT;
defaultPrompts.forEach((prompt) => {
this.allPrompts = [];
this.listener = this.listener.bind(this);
}
async initialize() {
this.allPrompts = [];
this.rootMenu = [];
this.menu_listeners = {};
this.allPrompts = await getPrompts(true);
this.allPrompts.forEach((prompt) => {
this.addAction(prompt)
});
//this.addMenu(this.rootMenu);
}
async reload(){
await browser.menus.removeAll().catch(error => {
console.error(">>>>>>>> ERROR removing the menus: ", error);
});
this.removeClickListener();
this.loadMenus();
}
addAction = (curr_prompt) => {
let curr_menu_entry = {id: curr_prompt.id };
let curr_menu_entry = {id: curr_prompt.id, is_default: curr_prompt.is_default, name: curr_prompt.name};
const getHighlight = async () => {
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
@ -56,7 +76,7 @@ export class mzta_Menus {
const msg_text = await getHighlight();
//check if a selection is needed
if(curr_prompt.need_selected && (msg_text.selection==='')){
if(String(curr_prompt.need_selected) == "1" && (msg_text.selection==='')){
//A selection is needed, but nothing is selected!
//alert(browser.i18n.getMessage('prompt_selection_needed'));
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
@ -75,7 +95,7 @@ export class mzta_Menus {
let prefs = await browser.storage.sync.get({default_chatgpt_lang: getLanguageDisplayName(browser.i18n.getUILanguage())});
let chatgpt_lang = prefs.default_chatgpt_lang;
var fullPrompt = curr_prompt.text + (curr_prompt.need_signature ? " " + await this.getDefaultSignature():"") + " " + browser.i18n.getMessage("prompt_lang") + chatgpt_lang + ". \"" + body_text + "\" ";
var fullPrompt = curr_prompt.text + (String(curr_prompt.need_signature) == "1" ? " " + await this.getDefaultSignature():"") + " " + browser.i18n.getMessage("prompt_lang") + chatgpt_lang + ". \"" + body_text + "\" ";
switch(curr_prompt.id){
case 'prompt_translate_this':
@ -105,47 +125,66 @@ export class mzta_Menus {
}
}
loadMenus() {
this.addMenu(this.rootMenu);
let listeners = this.menu_listeners;
browser.menus.onClicked.addListener((info, tab) => {
listeners[info.menuItemId] && listeners[info.menuItemId] (info, tab);
});
async loadMenus() {
await this.initialize();
await this.addMenu(this.rootMenu);
this.addClickListener();
}
addMenu = (menu, root = null) => {
for (let item of menu) {
let {id, menu, act} = item;
listener(info, tab) {
let listeners = this.menu_listeners;
if (listeners[info.menuItemId]) {
listeners[info.menuItemId](info, tab);
}
}
browser.menus.create({
id: id,
title: this.getCustomTextAttribute(id) + browser.i18n.getMessage(id),
contexts: this.getContexts(id),
parentId: root
});
addClickListener() {
browser.menus.onClicked.addListener(this.listener);
}
removeClickListener() {
browser.menus.onClicked.removeListener(this.listener);
}
addMenu = async (menu, root = null) => {
for (let item of menu) {
let {id, is_default, name, menu, act} = item;
await new Promise(resolve =>
browser.menus.create({
id: id,
title: this.getCustomTextAttribute(id) + is_default == 1 ? (browser.i18n.getMessage(id) || name) : name,
contexts: this.getContexts(id),
parentId: root
},
resolve
)
);
if (act) {
this.menu_listeners[id] = act;
}
if (menu) {
this.addMenu(menu, id);
await this.addMenu(menu, id);
}
}
};
getContexts(id){
const curr_prompt = defaultPrompts.find(p => p.id === id);
//console.log(">>>>>>>>> id: " + id);
const curr_prompt = this.allPrompts.find(p => p.id === id);
//console.log(">>>>>>>>>> curr_prompt: " + JSON.stringify(curr_prompt));
if (!curr_prompt) {
return [];
}
switch(curr_prompt.type){
case 0:
switch(String(curr_prompt.type)){
case "0":
return [this.menu_context_compose, this.menu_context_display];
case 1:
case "1":
return [this.menu_context_display];
case 2:
case "2":
return [this.menu_context_compose];
default:
return [];
@ -154,11 +193,11 @@ export class mzta_Menus {
}
getCustomTextAttribute(id){
const curr_prompt = defaultPrompts.find(p => p.id === id);
const curr_prompt = this.allPrompts.find(p => p.id === id);
if (!curr_prompt) {
return "";
}
if(curr_prompt.need_custom_text === 1){
if(String(curr_prompt.need_custom_text) === "1"){
return "*";
}else{
return "";

View file

@ -16,9 +16,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Modified version derived from https://github.com/ali-raheem/Aify/blob/13ff87583bc520fb80f555ab90a90c5c9df797a7/plugin/html/globals.js
// Modified prompts text derived from https://github.com/ali-raheem/Aify/blob/13ff87583bc520fb80f555ab90a90c5c9df797a7/plugin/html/globals.js
/* Types (type attribute):
/* ================= PROMPTS PROPERTIES ========================================
================ BASE PROPERTIES
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)
@ -39,14 +43,166 @@
Custom Text (need_custom_text attribute):
0: No custom text needed
1: Custom text needed
================ USER PROPERTIES
Enabled (enabled attribute):
0: Disabled
1: Enabled
Position Display Message (position_display attribute):
<num>: position number
Position Compose Message (position_compose attribute):
<num>: position number
*/
export const defaultPrompts = [
{ id: 'prompt_reply', name: "__MSG_prompt_reply__", text: "Reply to the following email.", type: 1, action: 1, need_selected: 0, need_signature: 1, need_custom_text: 1 }, // need_custom_text set to 1 only for testing
{ id: 'prompt_rewrite_polite', 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, need_signature: 0, need_custom_text: 0 },
{ id: 'prompt_rewrite_formal', 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, need_signature: 0, need_custom_text: 0 },
{ id: 'prompt_classify', 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, need_signature: 0, need_custom_text: 0 },
{ id: 'prompt_summarize_this', name: "__MSG_prompt_summarize_this__", text: "Summarize the following email into a bullet point list.", type: 0, action: 0, need_selected: 0, need_signature: 0, need_custom_text: 0 },
{ id: 'prompt_translate_this', name: "__MSG_prompt_translate_this__", text: "Translate the following email in ", type: 0, action: 0, need_selected: 0, need_signature: 0, need_custom_text: 0 },
{ id: 'prompt_this', name: "__MSG_prompt_this__", text: "Reply with only the needed text and with no extra comments or other text.", type: 2, action: 2, need_selected: 1, need_signature: 0, need_custom_text: 0 },
const defaultPrompts = [
{
id: 'prompt_reply',
name: "__MSG_prompt_reply__",
text: "Reply to the following email.",
type: "1",
action: "1",
need_selected: "0",
need_signature: "1",
need_custom_text: "0",
is_default: "1",
},
{
id: 'prompt_rewrite_polite',
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",
need_signature: "0",
need_custom_text: "0",
is_default: "1",
},
{
id: 'prompt_rewrite_formal',
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",
need_signature: "0",
need_custom_text: "0",
is_default: "1",
},
{
id: 'prompt_classify',
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",
need_signature: "0",
need_custom_text: "0",
is_default: "1",
},
{
id: 'prompt_summarize_this',
name: "__MSG_prompt_summarize_this__",
text: "Summarize the following email into a bullet point list.",
type: "0",
action: "0",
need_selected: "0",
need_signature: "0",
need_custom_text: "0",
is_default: "1",
},
{
id: 'prompt_translate_this',
name: "__MSG_prompt_translate_this__",
text: "Translate the following email in ",
type: "0",
action: "0",
need_selected: "0",
need_signature: "0",
need_custom_text: "0",
is_default: "1",
},
{
id: 'prompt_this',
name: "__MSG_prompt_this__",
text: "Reply with only the needed text and with no extra comments or other text.",
type: "2",
action: "2",
need_selected: "1",
need_signature: "0",
need_custom_text: "0",
is_default: "1",
},
];
export async function getPrompts(filterDisabled = false){
const defaultPrompts = await getDefaultPrompts_withProps();
const customPrompts = await getCustomPrompts();
let output = defaultPrompts.concat(customPrompts);
if(filterDisabled){
output = output.filter(obj => obj.enabled != 0);
}
output.sort((a, b) => a.id.localeCompare(b.id));
for(let i=1; i<=output.length; i++){
output[i-1].idnum = i;
}
return output;
}
async function getDefaultPrompts_withProps() {
let prefs = await browser.storage.sync.get({_default_prompts_properties: null}); //production
let defaultPrompts_prop = [...defaultPrompts];
if(prefs._default_prompts_properties === null){ // no default prompts properties saved
let pos = 1;
defaultPrompts_prop.forEach((prompt) => {
prompt.position_display = pos;
prompt.position_compose = pos;
prompt.enabled = 1;
pos++;
})
} else { // we have saved default prompts properties
let pos = 1000;
defaultPrompts_prop.forEach((prompt) => {
if(prefs._default_prompts_properties?.[prompt.id]){
prompt.position_compose = prefs._default_prompts_properties[prompt.id].position_compose;
prompt.position_display = prefs._default_prompts_properties[prompt.id].position_display;
prompt.enabled = prefs._default_prompts_properties[prompt.id].enabled;
}else{
prompt.position_display = pos;
prompt.position_compose = pos;
prompt.enabled = 1;
pos++;
}
})
}
return defaultPrompts_prop;
}
async function getCustomPrompts() {
let prefs = await browser.storage.sync.get({_custom_prompt: null});
if(prefs._custom_prompt === null){
return [];
} else {
return prefs._custom_prompt;
}
}
export async function setDefaultPromptsProperties(prompts) {
let default_prompts_properties = {};
prompts.forEach((prompt) => {
default_prompts_properties[prompt.id] = {position_compose: prompt.position_compose, position_display: prompt.position_display, enabled: prompt.enabled};
});
console.log('>>>>>>>>>>>>>> default_prompts_properties: ' + JSON.stringify(default_prompts_properties));
await browser.storage.sync.set({_default_prompts_properties: default_prompts_properties});
}
export async function setCustomPrompts(prompts) {
await browser.storage.sync.set({_custom_prompt: prompts});
}

View file

@ -16,6 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export const getMenuContextCompose = () => 'compose_action_menu';
export const getMenuContextDisplay = () => 'message_display_action_menu';
export function getLanguageDisplayName(languageCode) {
const languageDisplay = new Intl.DisplayNames([languageCode], {type: 'language'});

View file

@ -113,6 +113,10 @@ messenger.runtime.onMessage.addListener(async (message, sender, sendResponse) =>
await setBody(message.tabId, original_html);
await setBody(message.tabId, modified_html);
break;
case 'reload_menus':
await menus.reload();
console.log('>>>>>>>>>>>>>>>>> Reloaded menus');
break;
default:
break;
}
@ -162,6 +166,8 @@ async function openChatGPT(promptText, action, curr_tabId, do_custom_text = 0) {
});
let pre_script = `let mztaStatusPageDesc="`+ browser.i18n.getMessage("prefs_status_page") +`";
let mztaForceCompletionDesc="`+ browser.i18n.getMessage("chatgpt_force_completion") +`";
let mztaForceCompletionTitle="`+ browser.i18n.getMessage("chatgpt_force_completion_title") +`";
let mztaDoCustomText=`+ do_custom_text +`;
`;

View file

@ -1,3 +1,16 @@
@media (prefers-color-scheme: dark) {
body {
background-color: rgb(35, 34, 43);
color: rgb(251, 251, 254);
}
a:link { color: #409EFF; }
a:visited { color: #409EFF; }
a:hover { color: #66B1FF; }
a:active { color: #66B1FF; }
}
div#miczDescription{
padding:0px 15px 0px 15px;
font-size: 13px;
@ -71,4 +84,10 @@ input#chatgpt_win_height{
span.dims_label{
width: 75px;
display: inline-block;
}
#btn_custom_prompts{
text-align: center;
width: 100%;
margin: 10px 0px;
}

View file

@ -71,6 +71,9 @@
</td>
</tr>
</table>
<div id="btn_custom_prompts">
<button id="btnManagePrompts">__MSG_prefs_OptionText_btnManagePrompts__</button>
</div>
<div id="miczDescription">
<h1>__MSG_prefsInfoTitle__</h1>
<p>__MSG_prefsInfoDesc_1__<br/>

View file

@ -93,4 +93,16 @@ document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll(".option-input").forEach(element => {
element.addEventListener("change", saveOptions);
});
document.getElementById('btnManagePrompts').addEventListener('click', () => {
// check if the tab is already there
browser.tabs.query({url: browser.runtime.getURL('../customprompts/mzta-custom-prompts.html')}).then((tabs) => {
if (tabs.length > 0) {
// if the tab is already there, focus it
browser.tabs.update(tabs[0].id, {active: true});
} else {
// if the tab is not there, create it
browser.tabs.create({url: browser.runtime.getURL('../customprompts/mzta-custom-prompts.html')});
}
})
});
}, { once: true });

View file

@ -1,3 +1,16 @@
@media (prefers-color-scheme: dark) {
body {
background-color: rgb(35, 34, 43);
color: rgb(251, 251, 254);
}
a:link { color: #409EFF; }
a:visited { color: #409EFF; }
a:hover { color: #66B1FF; }
a:active { color: #66B1FF; }
}
div#miczRelNotes{
padding: 10px;
}

View file

@ -10,7 +10,13 @@
<h2>Version 1.1.0 - ??/??/2024</h2>
<ul>
<li>A prompt can be configured to request additional text from the user.</li>
<li>...</li>
<li>Added custom prompts. See more info <a href="https://micz.it/thunderdbird-addon-thunderai/custom-prompts/">here</a>.</li>
<li>Dark mode added.</li>
</ul>
<h2>Version 1.0.6 - 08/05/2024</h2>
<ul>
<li>Correctly handling a change in the ChatGPT web interface. See issue <a href="https://github.com/micz/ThunderAI/issues/43">#43</a></li>
<li>Added a button to force the completion to display the 'Use last answer' button if ChatGPT has finished its work but the button has not appeared.</li>
</ul>
<h2>Version 1.0.5 - 03/05/2024</h2>
<ul>