From 3d97444742d9e11b299d17bf9a8507cc52883cd9 Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:07:00 +0200 Subject: [PATCH 01/15] list.js moved to _lib folder --- pages/{customprompts => _lib}/list.js | 0 pages/customprompts/mzta-custom-prompts.html | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename pages/{customprompts => _lib}/list.js (100%) diff --git a/pages/customprompts/list.js b/pages/_lib/list.js similarity index 100% rename from pages/customprompts/list.js rename to pages/_lib/list.js diff --git a/pages/customprompts/mzta-custom-prompts.html b/pages/customprompts/mzta-custom-prompts.html index 1ac05ee5..6c847655 100644 --- a/pages/customprompts/mzta-custom-prompts.html +++ b/pages/customprompts/mzta-custom-prompts.html @@ -122,7 +122,7 @@
__MSG_StorageSpace__:
- + From b7d9dda1582f932f8bb4e17f87e6a1c61009fdf1 Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:07:00 +0200 Subject: [PATCH 02/15] Add button and functionality to manage data placeholders in options. see #156 --- _locales/en/messages.json | 4 ++++ options/mzta-options.html | 1 + options/mzta-options.js | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 1f1cfb1e..1a3ffdbe 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -315,6 +315,10 @@ "message": "Manage your prompts", "description": "" }, + "prefs_OptionText_btnManageCustomDataPH": { + "message": "Manage your data placeholders", + "description": "" + }, "prefsInfoTitle": { "message": "Important Information", "description": "" diff --git a/options/mzta-options.html b/options/mzta-options.html index 7cde8e5a..8c0db55f 100644 --- a/options/mzta-options.html +++ b/options/mzta-options.html @@ -543,6 +543,7 @@

__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_customPrompts_managePrompts_help__ +

__MSG_prefsInfoTitle__

diff --git a/options/mzta-options.js b/options/mzta-options.js index 9828e054..85e842d2 100644 --- a/options/mzta-options.js +++ b/options/mzta-options.js @@ -482,6 +482,10 @@ document.addEventListener('DOMContentLoaded', async () => { openTab('../pages/customprompts/mzta-custom-prompts.html'); }); + document.getElementById('btnManageCustomDataPH').addEventListener('click', () => { + openTab('../pages/customdataplaceholders/mzta-custom-dataplaceholders.html'); + }); + document.getElementById('btnManageTagsInfo').addEventListener('click', () => { openTab('../pages/addtags/mzta-add-tags.html'); }); From 528eb4c76c7aace5f1efe5bb6f1f6c104df6ebef Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:12:00 +0200 Subject: [PATCH 03/15] Refactor localization keys for prompts to use a consistent naming convention and update related HTML references --- _locales/cs/messages.json | 2 +- _locales/de/messages.json | 2 +- _locales/en/messages.json | 6 +++++- _locales/eo/messages.json | 2 +- _locales/fr/messages.json | 2 +- _locales/hr/messages.json | 2 +- _locales/it/messages.json | 2 +- _locales/pl/messages.json | 2 +- _locales/pt-br/messages.json | 2 +- _locales/ru/messages.json | 2 +- _locales/zh_Hans/messages.json | 2 +- _locales/zh_Hant/messages.json | 2 +- options/mzta-options.html | 10 +++++----- pages/addtags/mzta-add-tags.html | 2 +- pages/customprompts/mzta-custom-prompts.html | 4 ++-- pages/get-calendar-event/mzta-get-calendar-event.html | 2 +- pages/get-task/mzta-get-task.html | 2 +- pages/spamfilter/mzta-spamfilter.html | 2 +- 18 files changed, 27 insertions(+), 23 deletions(-) diff --git a/_locales/cs/messages.json b/_locales/cs/messages.json index 0291cc1d..6e219597 100644 --- a/_locales/cs/messages.json +++ b/_locales/cs/messages.json @@ -41,7 +41,7 @@ "prompt_this": { "message": "Zeptat se" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "Další informace" }, "customPrompts_start_saving": { diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 57c1e44f..601698bf 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -39,7 +39,7 @@ "customPrompts_managePrompts": { "message": "Eingabeaufforderungen verwalten" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "Informationen" }, "customPrompts_managePrompts_info_default": { diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 1a3ffdbe..67e8a53c 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -51,7 +51,7 @@ "message": "Manage Prompts", "description": "" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "More info", "description": "" }, @@ -1559,5 +1559,9 @@ "prefs_OptionText_add_tags_exclusions_exact_match_Info": { "message": "If checked, the tags in the exclusion list will be matched exactly, otherwise they will be matched partially.", "description": "" + }, + "customDataPH_manageDataPH": { + "message": "Manage Data Placeholders", + "description": "" } } \ No newline at end of file diff --git a/_locales/eo/messages.json b/_locales/eo/messages.json index 6ddc1ce7..4faf98cd 100644 --- a/_locales/eo/messages.json +++ b/_locales/eo/messages.json @@ -8,7 +8,7 @@ "prompt_rewrite_formal": { "message": "Reverki formale" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "Pliaj informoj" }, "customPrompts_managePrompts": { diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index 6484d226..bf6098b8 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -39,7 +39,7 @@ "customPrompts_managePrompts": { "message": "Gérer les prompts" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "Informations" }, "customPrompts_managePrompts_info_default": { diff --git a/_locales/hr/messages.json b/_locales/hr/messages.json index 1b3c3536..1370fb72 100644 --- a/_locales/hr/messages.json +++ b/_locales/hr/messages.json @@ -39,7 +39,7 @@ "customPrompts_managePrompts": { "message": "Upravljaj upitima" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "Više informacija" }, "customPrompts_managePrompts_info_default": { diff --git a/_locales/it/messages.json b/_locales/it/messages.json index ab5c7dc4..03cc95ff 100644 --- a/_locales/it/messages.json +++ b/_locales/it/messages.json @@ -39,7 +39,7 @@ "customPrompts_managePrompts": { "message": "Gestisci i prompt" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "Informazioni" }, "customPrompts_managePrompts_info_default": { diff --git a/_locales/pl/messages.json b/_locales/pl/messages.json index 9f34bd7b..19e300dd 100644 --- a/_locales/pl/messages.json +++ b/_locales/pl/messages.json @@ -39,7 +39,7 @@ "customPrompts_managePrompts": { "message": "Zarządzaj poleceniami" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "Więcej informacji" }, "customPrompts_managePrompts_info_default": { diff --git a/_locales/pt-br/messages.json b/_locales/pt-br/messages.json index 554e1246..8836ecb3 100644 --- a/_locales/pt-br/messages.json +++ b/_locales/pt-br/messages.json @@ -39,7 +39,7 @@ "customPrompts_managePrompts": { "message": "Gerenciar Prompts" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "Mais informações" }, "customPrompts_managePrompts_info_default": { diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json index 4f1be5f7..4d2bc835 100644 --- a/_locales/ru/messages.json +++ b/_locales/ru/messages.json @@ -26,7 +26,7 @@ "ChatGPT_Models_Fetch": { "message": "Обновить список моделей ChatGPT" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "Дополнительная информация" }, "customPrompts_btnEdit": { diff --git a/_locales/zh_Hans/messages.json b/_locales/zh_Hans/messages.json index 6b23f5d7..e837a7a1 100644 --- a/_locales/zh_Hans/messages.json +++ b/_locales/zh_Hans/messages.json @@ -65,7 +65,7 @@ "customPrompts_managePrompts": { "message": "管理提示词" }, - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "更多信息" }, "customPrompts_managePrompts_info_default": { diff --git a/_locales/zh_Hant/messages.json b/_locales/zh_Hant/messages.json index b898f350..07197248 100644 --- a/_locales/zh_Hant/messages.json +++ b/_locales/zh_Hant/messages.json @@ -1,5 +1,5 @@ { - "customPrompts_managePrompts_help": { + "more_info_string": { "message": "更多資訊" }, "prompt_reply_advanced": { diff --git a/options/mzta-options.html b/options/mzta-options.html index 8c0db55f..7796cad0 100644 --- a/options/mzta-options.html +++ b/options/mzta-options.html @@ -12,7 +12,7 @@
__MSG_prefs_SurveyLinkText__
__MSG_prefs_SurveyLinkText2__
-
__MSG_prefs_OptionText_owl_warning__ __MSG_customPrompts_managePrompts_help__
+
__MSG_prefs_OptionText_owl_warning__ __MSG_more_info_string__
__MSG_hyprland_warning__ @@ -297,7 +297,7 @@ - __MSG_remember_CORS__ [__MSG_customPrompts_managePrompts_help__] + __MSG_remember_CORS__ [__MSG_more_info_string__]

__MSG_CORS_alternative_1__
__MSG_CORS_alternative_2__

@@ -365,7 +365,7 @@ - __MSG_maybe_CORS_openai_comp__ [__MSG_customPrompts_managePrompts_help__] + __MSG_maybe_CORS_openai_comp__ [__MSG_more_info_string__]

__MSG_CORS_alternative_1__
__MSG_CORS_alternative_2__

@@ -542,7 +542,7 @@
-
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_customPrompts_managePrompts_help__ +
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__
@@ -550,7 +550,7 @@

__MSG_prefsInfoDesc_1__
__MSG_prefsInfoDesc_2__
- __MSG_prefsInfoDesc_3__ [__MSG_customPrompts_managePrompts_help__]
+ __MSG_prefsInfoDesc_3__ [__MSG_more_info_string__]
__MSG_prefsInfoDesc_4__
__MSG_prefsInfoDesc_5__
__MSG_prefsInfoDesc_6__
diff --git a/pages/addtags/mzta-add-tags.html b/pages/addtags/mzta-add-tags.html index 593116b7..ef5d2488 100644 --- a/pages/addtags/mzta-add-tags.html +++ b/pages/addtags/mzta-add-tags.html @@ -98,7 +98,7 @@

__MSG_AddTags_prompt_text_title__ -
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_customPrompts_managePrompts_help__ +
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__
__MSG_prefs_OptionText_btnManagePrompts_infoline3__
__MSG_prefs_OptionText_AdvancedPromptResponse_infoline2__

diff --git a/pages/customprompts/mzta-custom-prompts.html b/pages/customprompts/mzta-custom-prompts.html index 6c847655..eb607931 100644 --- a/pages/customprompts/mzta-custom-prompts.html +++ b/pages/customprompts/mzta-custom-prompts.html @@ -11,8 +11,8 @@

__MSG_customPrompts_managePrompts__

__MSG_customPrompts_managePrompts_info_default__
__MSG_customPrompts_managePrompts_info_default_2__
- __MSG_customPrompts_managePrompts_info_default_3__ __MSG_customPrompts_managePrompts_help__ -
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_customPrompts_managePrompts_help__

+ __MSG_customPrompts_managePrompts_info_default_3__ __MSG_more_info_string__ +
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__


diff --git a/pages/get-calendar-event/mzta-get-calendar-event.html b/pages/get-calendar-event/mzta-get-calendar-event.html index 911d0682..10e6d1cc 100644 --- a/pages/get-calendar-event/mzta-get-calendar-event.html +++ b/pages/get-calendar-event/mzta-get-calendar-event.html @@ -57,7 +57,7 @@
__MSG_GetCalendarEvent_prompt_text_title__ -
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_customPrompts_managePrompts_help__ +
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__
__MSG_prefs_OptionText_AdvancedPromptResponse_infoline2__

diff --git a/pages/get-task/mzta-get-task.html b/pages/get-task/mzta-get-task.html index f5f32028..6e8e5f15 100644 --- a/pages/get-task/mzta-get-task.html +++ b/pages/get-task/mzta-get-task.html @@ -57,7 +57,7 @@
__MSG_GetCalendarEvent_prompt_text_title__ -
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_customPrompts_managePrompts_help__ +
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__
__MSG_prefs_OptionText_AdvancedPromptResponse_infoline2__

diff --git a/pages/spamfilter/mzta-spamfilter.html b/pages/spamfilter/mzta-spamfilter.html index 1c82ac12..9c7b4fd3 100644 --- a/pages/spamfilter/mzta-spamfilter.html +++ b/pages/spamfilter/mzta-spamfilter.html @@ -34,7 +34,7 @@
__MSG_SpamFilter_prompt_text_title__ -
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_customPrompts_managePrompts_help__ +
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__
__MSG_prefs_OptionText_AdvancedPromptResponse_infoline2__

From 509b8de8e025dda37b6302f3c2cccff4b4892001 Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:12:00 +0200 Subject: [PATCH 04/15] Refactor localization keys for prompts to use a consistent naming convention and update related HTML references --- _locales/cs/messages.json | 4 ++-- _locales/de/messages.json | 4 ++-- _locales/en/messages.json | 16 ++++++++++++++-- _locales/eo/messages.json | 4 ++-- _locales/fr/messages.json | 4 ++-- _locales/hr/messages.json | 4 ++-- _locales/it/messages.json | 4 ++-- _locales/pl/messages.json | 4 ++-- _locales/pt-br/messages.json | 4 ++-- _locales/ru/messages.json | 4 ++-- _locales/zh_Hans/messages.json | 4 ++-- _locales/zh_Hant/messages.json | 4 ++-- pages/customprompts/mzta-custom-prompts.html | 4 ++-- 13 files changed, 38 insertions(+), 26 deletions(-) diff --git a/_locales/cs/messages.json b/_locales/cs/messages.json index 6e219597..a09cbf60 100644 --- a/_locales/cs/messages.json +++ b/_locales/cs/messages.json @@ -80,13 +80,13 @@ "customPrompts_form_label_enabled": { "message": "Povoleno" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Uložit vše" }, "customPrompts_do_reply": { "message": "Odpovědět" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Přidat nový" }, "customPrompts_close_button": { diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 601698bf..8a4f5927 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -120,10 +120,10 @@ "customPrompts_unsaved_changes": { "message": "Es gibt ungespeicherte Änderungen!" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Alle speichern" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Neu hinzufügen" }, "customPrompts_btnAddNewCommit": { diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 67e8a53c..e5a0c071 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -167,11 +167,11 @@ "message": "There are unsaved changes!", "description": "" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Save All", "description": "" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Add New", "description": "" }, @@ -1563,5 +1563,17 @@ "customDataPH_manageDataPH": { "message": "Manage Data Placeholders", "description": "" + }, + "customDataPH_manageDataPH_info_default": { + "message": "You can import and export the custom data placeholders. Existing data placeholders with the same ID will be overwritten. Placeholders with new IDs will be added.", + "description": "" + }, + "customDataPH_ExportAll": { + "message": "Export all custom data placeholders", + "description": "" + }, + "customDataPH_Import": { + "message": "Import new data placeholders", + "description": "" } } \ No newline at end of file diff --git a/_locales/eo/messages.json b/_locales/eo/messages.json index 4faf98cd..4624c571 100644 --- a/_locales/eo/messages.json +++ b/_locales/eo/messages.json @@ -20,10 +20,10 @@ "customPrompts_form_required_fields": { "message": "Postulataj kampoj" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Konservi ĉion" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Aldoni novan" }, "chatgpt_btn_retry": { diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index bf6098b8..eb2517e4 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -120,10 +120,10 @@ "customPrompts_unsaved_changes": { "message": "Il y a des modifications non enregistrées !" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Tout enregistrer" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Ajouter nouveau" }, "customPrompts_btnAddNewCommit": { diff --git a/_locales/hr/messages.json b/_locales/hr/messages.json index 1370fb72..4fae5f20 100644 --- a/_locales/hr/messages.json +++ b/_locales/hr/messages.json @@ -120,10 +120,10 @@ "customPrompts_unsaved_changes": { "message": "Ima nespremljenih promjena!" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Spremi sve" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Dodaj novo" }, "customPrompts_btnAddNewCommit": { diff --git a/_locales/it/messages.json b/_locales/it/messages.json index 03cc95ff..84386a6d 100644 --- a/_locales/it/messages.json +++ b/_locales/it/messages.json @@ -120,10 +120,10 @@ "customPrompts_unsaved_changes": { "message": "Ci sono modifiche non salvate!" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Salva Tutto" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Aggiungi Nuovo" }, "customPrompts_btnAddNewCommit": { diff --git a/_locales/pl/messages.json b/_locales/pl/messages.json index 19e300dd..6443d310 100644 --- a/_locales/pl/messages.json +++ b/_locales/pl/messages.json @@ -120,10 +120,10 @@ "customPrompts_unsaved_changes": { "message": "Istnieją niezapisane zmiany!" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Zapisz wszystko" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Dodaj nowe" }, "customPrompts_btnAddNewCommit": { diff --git a/_locales/pt-br/messages.json b/_locales/pt-br/messages.json index 8836ecb3..f6a94f1a 100644 --- a/_locales/pt-br/messages.json +++ b/_locales/pt-br/messages.json @@ -120,10 +120,10 @@ "customPrompts_unsaved_changes": { "message": "Existem alterações não salvas!" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Salvar Tudo" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Adicionar Novo" }, "customPrompts_btnAddNewCommit": { diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json index 4d2bc835..6ad482be 100644 --- a/_locales/ru/messages.json +++ b/_locales/ru/messages.json @@ -110,10 +110,10 @@ "customPrompts_btnDelete_confirmText": { "message": "Вы уверены, что хотите удалить этот элемент?" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "Сохранить все" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "Добавить новый" }, "customPrompts_btnAddNewCommit": { diff --git a/_locales/zh_Hans/messages.json b/_locales/zh_Hans/messages.json index e837a7a1..33fa33d0 100644 --- a/_locales/zh_Hans/messages.json +++ b/_locales/zh_Hans/messages.json @@ -38,13 +38,13 @@ "customPrompts_form_label_need_signature": { "message": "始终添加签名" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "新增" }, "chatgpt_win_job_completed": { "message": "完成了!" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "保存所有变动" }, "extensionDescription": { diff --git a/_locales/zh_Hant/messages.json b/_locales/zh_Hant/messages.json index 07197248..4741f617 100644 --- a/_locales/zh_Hant/messages.json +++ b/_locales/zh_Hant/messages.json @@ -113,10 +113,10 @@ "customPrompts_unsaved_changes": { "message": "有未儲存的變動!" }, - "customPrompts_btnSaveAll": { + "btnSaveAll_string": { "message": "儲存全部" }, - "customPrompts_btnNew": { + "btnNew_string": { "message": "新增" }, "customPrompts_btnAddNewCommit": { diff --git a/pages/customprompts/mzta-custom-prompts.html b/pages/customprompts/mzta-custom-prompts.html index eb607931..30049b14 100644 --- a/pages/customprompts/mzta-custom-prompts.html +++ b/pages/customprompts/mzta-custom-prompts.html @@ -19,9 +19,9 @@
- + - +
From 2351034e3ced45ce0dbf508a7d2ebebdc24a3cfb Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:17:00 +0200 Subject: [PATCH 05/15] Rename getCustomPromptsUsedSpace to getLocalStorageUsedSpace for clarity and update references --- js/mzta-utils.js | 2 +- pages/customprompts/mzta-custom-prompts.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/mzta-utils.js b/js/mzta-utils.js index b044d2db..b8d1b4ac 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -587,7 +587,7 @@ const insertHtml = function (replyHtml, fullBody_string) { return fullBody.body.innerHTML; } -export async function getCustomPromptsUsedSpace(){ +export async function getLocalStorageUsedSpace(){ // we can't use this, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1385832 //let customprompts_space = await browser.storage.local.getBytesInUse("_custom_prompt"); //let's use a workaround diff --git a/pages/customprompts/mzta-custom-prompts.js b/pages/customprompts/mzta-custom-prompts.js index 3b3bbeb2..faecf19b 100644 --- a/pages/customprompts/mzta-custom-prompts.js +++ b/pages/customprompts/mzta-custom-prompts.js @@ -18,7 +18,7 @@ import { prefs_default } from "../../options/mzta-options-default.js"; import { getPrompts, setDefaultPromptsProperties, setCustomPrompts, preparePromptsForExport, preparePromptsForImport } from "../../js/mzta-prompts.js"; -import { ChatGPTWeb_models, isThunderbird128OrGreater, getCustomPromptsUsedSpace, sanitizeHtml, validateCustomData_ChatGPTWeb, getChatGPTWebModelsList_HTML } from "../../js/mzta-utils.js"; +import { ChatGPTWeb_models, isThunderbird128OrGreater, getLocalStorageUsedSpace, sanitizeHtml, validateCustomData_ChatGPTWeb, getChatGPTWebModelsList_HTML } from "../../js/mzta-utils.js"; import { taLogger } from "../../js/mzta-logger.js"; import { getPlaceholders } from "../../js/mzta-placeholders.js"; import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js"; @@ -936,7 +936,7 @@ function clearMessage() { } async function setStorageSpace() { - let storage_space = await getCustomPromptsUsedSpace(); + let storage_space = await getLocalStorageUsedSpace(); document.getElementById('storage_space').textContent = storage_space; } From bed5c65de04b99558887738f667848e990afa21d Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:22:00 +0200 Subject: [PATCH 06/15] refactored many functions to work with custom placeholders. see #156 --- js/mzta-placeholders.js | 101 ++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 29 deletions(-) diff --git a/js/mzta-placeholders.js b/js/mzta-placeholders.js index c89dd813..40314dc1 100644 --- a/js/mzta-placeholders.js +++ b/js/mzta-placeholders.js @@ -41,6 +41,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'mail_html_body', @@ -48,6 +49,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'mail_typed_text', @@ -55,6 +57,7 @@ const defaultPlaceholders = [ default_value: "", type: 2, is_default: "1", + enabled: 1, }, { id: 'mail_quoted_text', @@ -62,6 +65,7 @@ const defaultPlaceholders = [ default_value: "", type: 2, is_default: "1", + enabled: 1, }, { id: 'mail_subject', @@ -69,6 +73,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'mail_folder_name', @@ -76,6 +81,7 @@ const defaultPlaceholders = [ default_value: "", type: 1, is_default: "1", + enabled: 1, }, { id: 'mail_folder_path', @@ -83,6 +89,7 @@ const defaultPlaceholders = [ default_value: "", type: 1, is_default: "1", + enabled: 1, }, { id: 'selected_text', @@ -90,6 +97,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'selected_html', @@ -97,6 +105,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'additional_text', @@ -104,6 +113,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'junk_score', @@ -111,6 +121,7 @@ const defaultPlaceholders = [ default_value: "0", type: 1, is_default: "1", + enabled: 1, }, { id: 'recipients', @@ -118,6 +129,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'cc_list', @@ -125,6 +137,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'author', @@ -132,6 +145,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'mail_datetime', @@ -139,6 +153,7 @@ const defaultPlaceholders = [ default_value: "", type: 1, is_default: "1", + enabled: 1, }, { id: 'current_datetime', @@ -146,6 +161,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'account_email_address', @@ -153,6 +169,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'tags_current_email', @@ -160,6 +177,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'tags_full_list', @@ -167,6 +185,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'thunderai_def_sign', @@ -174,6 +193,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'thunderai_def_lang', @@ -181,6 +201,7 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, { id: 'empty', @@ -188,12 +209,13 @@ const defaultPlaceholders = [ default_value: "", type: 0, is_default: "1", + enabled: 1, }, ]; export async function getPlaceholders(onlyEnabled = false){ - const _defaultPlaceholders = await getDefaultPlaceholders_withProps(); + const _defaultPlaceholders = await getDefaultPlaceholders(); // console.log('>>>>>>>>>>>> getPlaceholders _defaultPlaceholders: ' + JSON.stringify(_defaultPlaceholders)); const customPlaceholders = await getCustomPlaceholders(); // console.log('>>>>>>>>>>>> getPlaceholders customPlaceholders: ' + JSON.stringify(customPlaceholders)); @@ -207,32 +229,14 @@ export async function getPlaceholders(onlyEnabled = false){ return output; } -async function getDefaultPlaceholders_withProps() { - let prefs = await browser.storage.local.get({_default_placeholders_properties: null}); - // console.log('>>>>>>>>>>>> getDefaultPlaceholders_withProps prefs: ' + JSON.stringify(prefs)); - //let defaultPlaceholders_prop = [...defaultPlaceholders]; +async function getDefaultPlaceholders() { let defaultPlaceholders_prop = JSON.parse(JSON.stringify(defaultPlaceholders)); // console.log('>>>>>>>>>>>> getDefaultPlaceholders_withProps defaultPlaceholders: ' + JSON.stringify(defaultPlaceholders)); // console.log('>>>>>>>>>>>> getDefaultPlaceholders_withProps defaultPlaceholders_prop: ' + JSON.stringify(defaultPlaceholders_prop)); - if(prefs._default_placeholders_properties === null){ // no default Placeholders properties saved - defaultPlaceholders_prop.forEach((placeholder) => { - placeholder.enabled = 1; - }) - // console.log('>>>>>>>>>>>> getDefaultPlaceholders_withProps [no prop saved] defaultPlaceholders_prop: ' + JSON.stringify(defaultPlaceholders_prop)); - } else { // we have saved default Placeholders properties - defaultPlaceholders_prop.forEach((placeholder) => { - if(prefs._default_Placeholders_properties?.[placeholder.id]){ - placeholder.enabled = prefs._default_Placeholders_properties[placeholder.id].enabled; - }else{ - placeholder.enabled = 1; - } - }) - // console.log('>>>>>>>>>>>> getDefaultPlaceholders_withProps [prop saved] defaultPlaceholders_prop: ' + JSON.stringify(defaultPlaceholders_prop)); - } return defaultPlaceholders_prop; } -async function getCustomPlaceholders() { +export async function getCustomPlaceholders() { let prefs = await browser.storage.local.get({_custom_placeholder: null}); if(prefs._custom_placeholder === null){ return []; @@ -241,22 +245,61 @@ async function getCustomPlaceholders() { } } -export async function setDefaultPlaceholdersProperties(placeholders) { - let default_placeholders_properties = {}; - placeholders.forEach((placeholder) => { - default_placeholders_properties[placeholder.id] = {enabled: placeholder.enabled}; +export async function setCustomPlaceholders(placeholders) { + placeholders.forEach(ph => { + ph.id = placeholdersUtils.validateCustomDataPH_ID(ph.id); + ph.is_default = "0"; }); - //console.log('>>>>>>>>>>>>>> default_placeholders_properties: ' + JSON.stringify(default_placeholders_properties)); - await browser.storage.local.set({_default_placeholders_properties: default_placeholders_properties}); + await browser.storage.local.set({_custom_placeholder: placeholders}); } -export async function setCustomPlaceholders(placeholders) { - await browser.storage.local.set({_custom_placeholder: placeholders}); +export function prepareCustomDataPHsForExport(placeholders){ + let output = [...placeholders]; + output.forEach(placeholder => { + if(placeholder.is_default == 0){ + delete placeholder['idnum']; + } + }); + return output; +} + +export async function prepareCustomDataPHsForImport(placeholders){ + // console.log(">>>>>>>>>>> prepareCustomDataPHsForImport prompts: " + JSON.stringify(prompts)); + const output = await getCustomPlaceholders(); + // console.log(">>>>>>>>>>> prepareCustomDataPHsForImport output: " + JSON.stringify(output)); + prompts.forEach(placeholder => { + if(output.some(p => p.id == placeholder.id)){ + Object.keys(placeholder).forEach(key => { + output.find(p => p.id == placeholder.id)[key] = placeholder[key]; + }) + }else{ + output.push(placeholder); + } + }); + output.sort((a, b) => a.id.localeCompare(b.id)); + // console.log(">>>>>>>>>>> prepareCustomDataPHsForImport final output: " + JSON.stringify(output)); + return output; } export const placeholdersUtils = { + customPrefix: 'thunderai_custom_', + + validateCustomDataPH_ID(placeholder_id){ + if (!placeholder_id.startsWith(this.customPrefix)) { + return this.customPrefix + placeholder_id; + } + return placeholder_id; + }, + + stripCustomDataPH_ID_Prefix(placeholder_id){ + if (placeholder_id.startsWith(this.customPrefix)) { + return placeholder_id.substring(this.customPrefix.length); + } + return placeholder_id; + }, + async extractPlaceholders(text) { // Regular expression to match patterns like {%...%} const regex = /{%\s*(.*?)\s*%}/g; From 1b5dea925320b48f4220e5cc589288fe42aebe6c Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:27:00 +0200 Subject: [PATCH 07/15] working on #156. saving and loading custom placeholders in the dedicated page --- _locales/en/messages.json | 16 + .../mzta-custom-dataplaceholders.css | 360 ++++++++++ .../mzta-custom-dataplaceholders.html | 91 +++ .../mzta-custom-dataplaceholders.js | 628 ++++++++++++++++++ 4 files changed, 1095 insertions(+) create mode 100644 pages/customdataplaceholders/mzta-custom-dataplaceholders.css create mode 100644 pages/customdataplaceholders/mzta-custom-dataplaceholders.html create mode 100644 pages/customdataplaceholders/mzta-custom-dataplaceholders.js diff --git a/_locales/en/messages.json b/_locales/en/messages.json index e5a0c071..21e142a7 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1575,5 +1575,21 @@ "customDataPH_Import": { "message": "Import new data placeholders", "description": "" + }, + "customDataPH_form_label_Text": { + "message": "Data Placeholder Text", + "description": "" + }, + "customDataPH_saving_custom": { + "message": "Saving custom data placeholders...", + "description": "" + }, + "customDataPH_saved": { + "message": "Custom data placeholders saved!", + "description": "" + }, + "customDataPH_btnAddNewCommit": { + "message": "Add the Data Placeholder", + "description": "" } } \ No newline at end of file diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.css b/pages/customdataplaceholders/mzta-custom-dataplaceholders.css new file mode 100644 index 00000000..888459df --- /dev/null +++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.css @@ -0,0 +1,360 @@ +@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; + } +} + +table { + border-collapse: collapse; + border-spacing: 0; + border: 1px solid #ccc; +} + +thead { + position: sticky; + top: 26px; + z-index: 90; +} + +#command_palette{ + position: sticky; + top: 0; + padding-top: 1px; + height: 26px; + width: 100%; + z-index: 90; +} + +#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); +} + +tbody.list tr{ + vertical-align: top; +} + +.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; +} + +.field_title{ + font-weight: bold; +} + +.field_title_s{ + font-weight: bold; + font-size: small; +} + +.field_title_us{ + font-weight: bold; + font-size: smaller; +} + +label, .id_show, .text_show, .name_show, .type_show, .action_show{ + font-size: small; +} + +#txtIdNew, .id_output{ + text-transform: lowercase; +} + +#import_export{ + position: absolute; + right:10px; + top:10px; + text-align: right; +} + +#btnExportAll{ + margin-bottom: 3px; +} + +.storage_space{ + font-size: 0.8em; + font-weight: normal; + font-style: italic; + color: rgb(41, 41, 41); + text-align: right; + margin-right: 1px; +} + +.page_title{ + margin-bottom: 0px; +} + +.autocomplete-container { + position: relative; +} + +.autocomplete-list { + position: absolute; + top: 100%; + left: 0; + right: 0; + background-color: white; + border: 1px solid #ccc; + z-index: 1000; + max-height: 200px; + overflow-y: auto; + padding: 0; + margin: 0; + list-style: none; + font-size: small; +} + +.autocomplete-list li { + padding: 8px; + cursor: pointer; +} + +.autocomplete-list li:hover { + background-color: #f0f0f0; +} + +.autocomplete-list li.active { + background-color: #ddd; +} + +.hidden { + display: none; +} + +.need_custom_text_span, .need_selected_span{ + padding-right: 2px; +} + +#chatgpt_web_additional_info_toggle td{ + padding:0px 5px; + font-style: italic; +} + +#chatgpt_web_additional_info, #chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info, .chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info_show{ + background-color: rgb(255, 209, 183); + display:none; +} + +.chatgpt_web_models_list_table{ + background-color: rgb(255, 209, 183); + border:0; +} + +.chatgpt_web_models_list_table *{ + border:0; +} + +.chatgpt_web_models_list_table td{ + padding:2px; +} + +#chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info_toggle{ + cursor: pointer; +} + +#chatgpt_web_additional_info_toggle:hover, .chatgpt_web_additional_info_toggle:hover{ + background-color: rgb(255, 189, 153); + text-decoration: underline; +} + +#chatgpt_web_additional_info_toggle td{ + text-align: center; +} + +#chatgpt_web_additional_info td{ + vertical-align: top; +} + +.chatgpt_web_additional_info_toggle{ + width: -moz-available; + text-align: center; + margin-top: 10px; +} + +.chatgpt_web_additional_info_show{ + flex-direction: column; + justify-content: space-between; + height: 100%; + padding: 3px; + margin-top: 5px; +} + +.chatgpt_web_additional_info_row{ + display: flex; +} + +.chatgpt_web_additional_info_row span{ + margin-right: 0.4em; +} + +input.input_additional[type="text"]{ + width: -moz-available; +} + +.small_info{ + font-size:smaller; +} + +.conntype_chatgpt_web_option{ + cursor: pointer; +} + +@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; + } + + .storage_space{ + color: rgb(182, 182, 182); + } + + .autocomplete-list { + background-color: #2E2F36; + border: 1px solid #2E2F36; + } + + .autocomplete-list li:hover { + background-color: #4c4e58; + } + + .autocomplete-list li.active { + background-color: #4c4e58; + } + + #chatgpt_web_additional_info, #chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info, .chatgpt_web_additional_info_toggle, .chatgpt_web_additional_info_show, .chatgpt_web_models_list_table{ + background-color: rgb(39, 11, 0); + color: rgb(182, 182, 182); + } + + #chatgpt_web_additional_info_toggle:hover, .chatgpt_web_additional_info_toggle:hover{ + background-color: rgb(88, 25, 0); + } +} \ No newline at end of file diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.html b/pages/customdataplaceholders/mzta-custom-dataplaceholders.html new file mode 100644 index 00000000..ea95d2b4 --- /dev/null +++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.html @@ -0,0 +1,91 @@ + + + + + ThunderAI - __MSG_customDataPH_manageDataPH__ + + + + +
+

__MSG_customDataPH_manageDataPH__

+

__MSG_customDataPH_manageDataPH_info_default__
+ __MSG_customPrompts_managePrompts_info_default_3__ +
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__

+
+
+
+ +
+
+ + + +
+
+
+ + + + + + + + + +
+ +
+ thunderai_custom_ +
[__MSG_customPrompts_form_label_ID_rules__] +
+ +
+
+ + +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
* __MSG_customPrompts_form_required_fields__
+
+
+
+
+ + + + + + + + + + + + +
__MSG_customPrompts_form_label_ID____MSG_customPrompts_form_label_Name____MSG_customDataPH_form_label_Text____MSG_customPrompts_Properties__ 
+
+
__MSG_StorageSpace__:
+ + + + + \ No newline at end of file diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.js b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js new file mode 100644 index 00000000..20f89755 --- /dev/null +++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js @@ -0,0 +1,628 @@ +/* + * ThunderAI [https://micz.it/thunderbird-addon-thunderai/] + * Copyright (C) 2024 - 2025 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 . + */ + +import { prefs_default } from "../../options/mzta-options-default.js"; +import { getLocalStorageUsedSpace, sanitizeHtml, isThunderbird128OrGreater } from "../../js/mzta-utils.js"; +import { taLogger } from "../../js/mzta-logger.js"; +import { getPlaceholders, setCustomPlaceholders, getCustomPlaceholders, prepareCustomDataPHsForExport, prepareCustomDataPHsForImport, placeholdersUtils } from "../../js/mzta-placeholders.js"; +import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js"; + +let prefs = null; +var customDataPHsList = null; +var somethingChanged = false; +var idnumMax = 0; +var msgTimeout = null; +let taLog = null; +let autocompleteSuggestions = []; + +document.addEventListener('DOMContentLoaded', async () => { + + prefs = await browser.storage.sync.get({ do_debug: prefs_default.do_debug }); + taLog = new taLogger("mzta-custom-dataplaceholders", prefs.do_debug); + + setStorageSpace(); + + let values = await getCustomPlaceholders(); + + //console.log('>>>>>>>>>>>>>>>> values: ' + JSON.stringify(values)); + + loadCustomDataPHsList(values); + + 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); + + // 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(); + }); + }); + } + + const textareas = document.querySelectorAll('.editor'); + autocompleteSuggestions = (await getPlaceholders()) + .filter(p => p.is_default == "1") + .map(p => ({ command: '{%' + p.id + '%}', type: p.type })); + + // console.log('>>>>>>>>>>> suggestions: ' + JSON.stringify(suggestions)); + + textareas.forEach(textarea => { + textareaAutocomplete(textarea, autocompleteSuggestions); + textareas.forEach(textarea => { + textarea.addEventListener('input', (e) => { + checkPromptsConfigForPlaceholders(e.target); + }); + textareaAutocomplete(textarea, autocompleteSuggestions); + }); + }); + + 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 checkboxNeedSelectedNew = document.getElementById('checkboxNeedSelectedNew'); + var checkboxNeedCustomTextNew = document.getElementById('checkboxNeedCustomTextNew'); + + const btnAddNew = document.getElementById('btnAddNew'); + btnAddNew.addEventListener('click', (e) => { + e.preventDefault(); + if(!checkFields()) { + return; + } + let newItemData = { + id: String(txtIdNew.value.trim()).toLocaleLowerCase(), + name: txtNameNew.value.trim(), + text: txtTextNew.value.trim(), + type: selectTypeNew.value, + need_selected: (checkboxNeedSelectedNew.checked) ? 1 : 0, + need_custom_text: (checkboxNeedCustomTextNew.checked) ? 1 : 0, + enabled: 1, + is_default: 0, + idnum: idnumMax + 1, + }; + + let newItem = customDataPHsList.add(newItemData); + 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' + }); + }); + + //Import Export + const btnExportAll = document.getElementById('btnExportAll'); + btnExportAll.addEventListener('click', (e) => { + e.preventDefault(); + exportPrompts(); + }); + + async function exportPrompts() { + const manifest = browser.runtime.getManifest(); + const addonVersion = manifest.version; + const outputPrompts = preparePromptsForExport(await getPrompts()); + let outputObj = {id: 'thunderai-prompts', addon_version: addonVersion, prompts: outputPrompts}; + const blob = new Blob([JSON.stringify(outputObj, null, 2)], { + type: "application/json", + }); + const currentDate = new Date(); + const time_stamp = `${currentDate.getFullYear()}${String(currentDate.getMonth() + 1).padStart(2, '0')}${String(currentDate.getDate()).padStart(2, '0')}${String(currentDate.getHours()).padStart(2, '0')}${String(currentDate.getMinutes()).padStart(2, '0')}${String(currentDate.getSeconds()).padStart(2, '0')}`; + messenger.downloads.download({ + url: URL.createObjectURL(blob), + filename: `thunderai-prompts-${time_stamp}.json`, + saveAs: true, + }); + } + + const btnImport = document.getElementById('btnImport'); + btnImport.addEventListener('click', (e) => { + e.preventDefault(); + importPrompts(); + }); + + function importPrompts() { + if(confirm(browser.i18n.getMessage("importPrompts_confirmText") + '\n' + browser.i18n.getMessage("customPrompts_managePrompts_info_default_2") + '\n' + browser.i18n.getMessage("customPrompts_managePrompts_info_default_3"))) { + //ask the user to choose a JSON file, and then read it, check if the serialized JSON is valid as generated from exportPrompts(), and if so, add it to the list + const input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; + input.click(); + input.onchange = async () => { + setMessage(browser.i18n.getMessage('customPrompts_start_import')); + const file = input.files[0]; + const reader = new FileReader(); + reader.onload = async () => { + const json = reader.result; + try { + const obj = JSON.parse(json); + if(obj.id !== 'thunderai-prompts') { + alert(browser.i18n.getMessage("importPrompts_invalidFile")); + setMessage(browser.i18n.getMessage('importPrompts_invalidFile'),'red'); + return; + } + // if(obj.addon_version !== manifest.version) { + // alert(browser.i18n.getMessage("importPrompts_invalidVersion")); + // return; + // } + if(!Array.isArray(obj.prompts)) { + alert(browser.i18n.getMessage("importPrompts_invalidPrompts")); + setMessage(browser.i18n.getMessage('customPrompts_invalidPrompts'),'red'); + return; + } + //setCustomPrompts(obj.prompts); + customDataPHsList.clear(); + loadCustomDataPHsList(await preparePromptsForImport(obj.prompts)); + setSomethingChanged(); + i18n.updateDocument(); + // browser.runtime.sendMessage({command: "reload_menus"}); + setMessage(browser.i18n.getMessage('customPrompts_import_completed'), 'orange'); + // msgTimeout = setTimeout(() => { + // clearMessage(); + // }, 10000); + } catch(err) { + alert(browser.i18n.getMessage("importPrompts_invalidFile") + ' ' + err); + setMessage(browser.i18n.getMessage('importPrompts_invalidFile'),'red'); + return; + } + }; + reader.readAsText(file); + }; + + }; + } + +}, { once: true }); + +//========= handling an item in a row +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); +} + +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'; + const text_output = tr.querySelector('.text_output'); + text_output.style.display = 'inline'; + textareaAutocomplete(text_output, autocompleteSuggestions) + tr.querySelector('.text_show').style.display = 'none'; + tr.querySelector('.type_output').style.display = 'inline'; + tr.querySelector('.type_show').style.display = 'none'; + tr.querySelector('input.need_selected').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('input.need_selected').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')); + customDataPHsList.remove("id", tr.querySelector('span.id').innerText); + setSomethingChanged(); +} + +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 + tr.querySelector('.id_output').value = tr.querySelector('.id_show').innerText.toLocaleUpperCase(); + tr.querySelector('.name_output').value = tr.querySelector('.name_show').innerText; + tr.querySelector('.text_output').value = sanitizeHtml(tr.querySelector('.text_show').innerHTML).replace(//gi, "\n"); + tr.querySelector('.type_output').value = tr.querySelector('.type').innerText; + hideItemRowEditor(tr); +} + +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; + // the checkboxes update is handled directly by themselves + hideItemRowEditor(tr); + setSomethingChanged(); +} + +// 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')); + if (e.target.classList.contains('need_selected') || e.target.classList.contains('need_custom_text') || e.target.classList.contains('need_selected_new') || e.target.classList.contains('need_custom_text_new')) { + let textarea = e.target.closest('tr').querySelector('.text_output'); + checkPromptsConfigForPlaceholders(textarea); + } + +} + +// Enable save button on input change +function handleInputChange(e) { + e.preventDefault(); + setSomethingChanged(); +} + +//========= handling an item in a row - END + + +function loadCustomDataPHsList(values){ + // console.log('>>>>>>>> loadCustomDataPHsList values: ' + JSON.stringify(values)); + let options = { + valueNames: [ + { data: ['idnum'] }, + 'is_default', + 'id', + 'name', + 'text', + 'type', + { name: 'need_selected', attr: 'checked_val'}, + { name: 'need_custom_text', attr: 'checked_val'}, + { name: 'enabled', attr: 'checked_val'} + ], + item: function(values) { + values.id = placeholdersUtils.stripCustomDataPH_ID_Prefix(values.id); + 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 output = ` + thunderai_custom_ + + + +
+ + +
+ + __MSG_customPrompts_add_to_menu__: +
+ ` + type_output + ` + ` + + ` + + + +
+ +
+ + + + + + + + +

+ + + + `; + //console.log('>>>>>>>> values.name: ' + JSON.stringify(values.name)); + idnumMax = Math.max(idnumMax, values.idnum); + return output; + } + }; + + // console.log('>>>>>>>>>>>>> options: ' + JSON.stringify(options)); + // console.log('>>>>>>>>>>>>> values: ' + JSON.stringify(values)); + + customDataPHsList = new List('all_custom_dataplaceholders', options, values); + + checkSelectedBoxes(); + let btnEditItem_elements = document.querySelectorAll(".btnEditItem"); + btnEditItem_elements.forEach(element => { + element.addEventListener('click', handleEditClick); + }); + + let btnDeleteItem_elements = document.querySelectorAll(".btnDeleteItem"); + btnDeleteItem_elements.forEach(element => { + element.addEventListener('click', handleDeleteClick); + }); + + let btnCancelItem_elements = document.querySelectorAll(".btnCancelItem"); + btnCancelItem_elements.forEach(element => { + element.addEventListener('click', handleCancelClick); + }); + + let btnConfirmItem_elements = document.querySelectorAll(".btnConfirmItem"); + btnConfirmItem_elements.forEach(element => { + element.addEventListener('click', handleConfirmClick); + }); + + let checkbox_elements = document.querySelectorAll("input[type='checkbox']"); + checkbox_elements.forEach(element => { + element.addEventListener('change', handleCheckboxChange); + }); + + document.querySelectorAll('.input_mod').forEach(element => { + element.addEventListener('change', handleInputChange); + }); +} + +function checkFields() { + //console.log('>>>>>>>>>>>>> typeof customDataPHsList: ' + typeof customDataPHsList); + //console.log('>>>>>>>>>>>>> Array.isArray(customDataPHsList): ' + Array.isArray(customDataPHsList)); + // 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 = customDataPHsList.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('checkboxNeedSelectedNew').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.textContent = 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.textContent = '' + msgDisplay.style.display = 'none'; + msgDisplay.style.color = ''; +} + +function checkSelectedBoxes(checkboxes = null) { + if(checkboxes == null){ + checkboxes = [ + ...document.querySelectorAll('.need_selected[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 custom data placeholders +async function saveAll() { + setMessage(browser.i18n.getMessage('customDataPH_start_saving')); + setNothingChanged(); + if(customDataPHsList != null) { + setMessage(browser.i18n.getMessage('customPrompts_reindexing_list')); + customDataPHsList.reIndex(); + let newCustomDataPHs = customDataPHsList.items.filter(item => item.values().is_default == 0).map(item => item.values()); + taLog.log('newCustomDataPlaceholders: ' + JSON.stringify(newCustomDataPHs)); + // newCustomDataPHs.forEach(prompt => { + // console.log('>>>>>>>>>>>>> id: ' + JSON.stringify(prompt)); + // }); + //console.log('>>>>>>>>>>>>> saveAll: ' + JSON.stringify(newCustomDataPHs)); + await setCustomPlaceholders(newCustomDataPHs); + setMessage(browser.i18n.getMessage('customDataPH_saved'),'green'); + msgTimeout = setTimeout(() => { + clearMessage(); + }, 10000) + } + setStorageSpace(); +} + +function setMessage(msg, color = '') { + clearTimeout(msgTimeout); + let msgDisplay = document.getElementById('msgDisplay'); + msgDisplay.textContent = msg; + msgDisplay.style.display = 'inline'; + msgDisplay.style.color = color; +} + +function clearMessage() { + let msgDisplay = document.getElementById('msgDisplay'); + msgDisplay.textContent = ''; + msgDisplay.style.display = 'none'; + msgDisplay.style.color = ''; +} + +async function setStorageSpace() { + let storage_space = await getLocalStorageUsedSpace(); + document.getElementById('storage_space').textContent = storage_space; +} + + +if(await isThunderbird128OrGreater()){ + window.addEventListener('beforeunload', function (event) { + // Check if any changes have been made (Only for Thunderbird 128+ see https://github.com/micz/ThunderAI/issues/88) + if (somethingChanged) { + event.preventDefault(); + } + }); +} + +function checkPromptsConfigForPlaceholders(textarea){ + // check additional_text and selected_text placeholders presence and the corrispondent checkboxes + let tr_ancestor = textarea.closest('tr'); + let need_custom_text_element = tr_ancestor.querySelector('.need_custom_text') || tr_ancestor.querySelector('.need_custom_text_new'); + if(String(textarea.value).indexOf('{%additional_text%}') != -1){ + if(!need_custom_text_element.checked){ + need_custom_text_element.closest('.need_custom_text_span').style.border = '2px solid red'; + }else{ + need_custom_text_element.closest('.need_custom_text_span').style.border = ''; + } + }else{ + need_custom_text_element.closest('.need_custom_text_span').style.border = ''; + } + + let tr_ancestor2 = textarea.closest('tr'); + let selected_text_element = tr_ancestor2.querySelector('.need_selected') || tr_ancestor2.querySelector('.need_selected_new'); + if((String(textarea.value).indexOf('{%selected_text%}') != -1)||(String(textarea.value).indexOf('{%selected_html%}') != -1)){ + if(!selected_text_element.checked){ + selected_text_element.closest('.need_selected_span').style.border = '2px solid red'; + }else{ + selected_text_element.closest('.need_selected_span').style.border = ''; + } + }else{ + selected_text_element.closest('.need_selected_span').style.border = ''; + } +} \ No newline at end of file From 8819391b77e16eb34c3643cf5862ce20532c4bce Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:32:00 +0200 Subject: [PATCH 08/15] i18n string fixed. see #156 --- pages/customdataplaceholders/mzta-custom-dataplaceholders.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.js b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js index 20f89755..e48d74fa 100644 --- a/pages/customdataplaceholders/mzta-custom-dataplaceholders.js +++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js @@ -550,7 +550,7 @@ function checkSelectedBoxes(checkboxes = null) { //Save all custom data placeholders async function saveAll() { - setMessage(browser.i18n.getMessage('customDataPH_start_saving')); + setMessage(browser.i18n.getMessage('customDataPH_saving_custom')); setNothingChanged(); if(customDataPHsList != null) { setMessage(browser.i18n.getMessage('customPrompts_reindexing_list')); From f0d2e12c7b3568f3579091dd2bd3a083e3d7b6b9 Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:32:00 +0200 Subject: [PATCH 09/15] var fixed --- _locales/en/messages.json | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 21e142a7..7cf15bfe 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1591,5 +1591,25 @@ "customDataPH_btnAddNewCommit": { "message": "Add the Data Placeholder", "description": "" + }, + "importCustomDataPH_confirmText": { + "message": "You are about to import new custom data placeholders.", + "description": "" + }, + "importCustomDataPH_start_import": { + "message": "Starting custom data placeholders import...", + "description": "" + }, + "importCustomDataPH_import_completed": { + "message": "Custom data placeholders import completed! You need to click on the 'Save All' button to save your changes.", + "description": "" + }, + "importCustomDataPH_invalidFile": { + "message": "The file you're trying to import is not a valid custom data placeholders file.", + "description": "" + }, + "importCustomDataPH_invalidDataPHs": { + "message": "The file you're trying to import does not contain any valid custom data placeholders.", + "description": "" } } \ No newline at end of file From f4c1ab1018b706bb8c3ed546e4b6f3f152d7ba97 Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:37:00 +0200 Subject: [PATCH 10/15] import/export data placeholders added. see #156 --- js/mzta-placeholders.js | 2 +- .../mzta-custom-dataplaceholders.js | 46 ++++++++----------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/js/mzta-placeholders.js b/js/mzta-placeholders.js index 40314dc1..1cec0dbf 100644 --- a/js/mzta-placeholders.js +++ b/js/mzta-placeholders.js @@ -267,7 +267,7 @@ export async function prepareCustomDataPHsForImport(placeholders){ // console.log(">>>>>>>>>>> prepareCustomDataPHsForImport prompts: " + JSON.stringify(prompts)); const output = await getCustomPlaceholders(); // console.log(">>>>>>>>>>> prepareCustomDataPHsForImport output: " + JSON.stringify(output)); - prompts.forEach(placeholder => { + placeholders.forEach(placeholder => { if(output.some(p => p.id == placeholder.id)){ Object.keys(placeholder).forEach(key => { output.find(p => p.id == placeholder.id)[key] = placeholder[key]; diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.js b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js index e48d74fa..cbfd37c0 100644 --- a/pages/customdataplaceholders/mzta-custom-dataplaceholders.js +++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js @@ -156,14 +156,14 @@ document.addEventListener('DOMContentLoaded', async () => { const btnExportAll = document.getElementById('btnExportAll'); btnExportAll.addEventListener('click', (e) => { e.preventDefault(); - exportPrompts(); + exportCustomDataPHs(); }); - async function exportPrompts() { + async function exportCustomDataPHs() { const manifest = browser.runtime.getManifest(); const addonVersion = manifest.version; - const outputPrompts = preparePromptsForExport(await getPrompts()); - let outputObj = {id: 'thunderai-prompts', addon_version: addonVersion, prompts: outputPrompts}; + const outputCustomDataPHs = prepareCustomDataPHsForExport(await getCustomPlaceholders()); + let outputObj = {id: 'thunderai-custom-data-placeholders', addon_version: addonVersion, customdataplaceholders: outputCustomDataPHs}; const blob = new Blob([JSON.stringify(outputObj, null, 2)], { type: "application/json", }); @@ -171,7 +171,7 @@ document.addEventListener('DOMContentLoaded', async () => { const time_stamp = `${currentDate.getFullYear()}${String(currentDate.getMonth() + 1).padStart(2, '0')}${String(currentDate.getDate()).padStart(2, '0')}${String(currentDate.getHours()).padStart(2, '0')}${String(currentDate.getMinutes()).padStart(2, '0')}${String(currentDate.getSeconds()).padStart(2, '0')}`; messenger.downloads.download({ url: URL.createObjectURL(blob), - filename: `thunderai-prompts-${time_stamp}.json`, + filename: `thunderai-custom-data-placeholders-${time_stamp}.json`, saveAs: true, }); } @@ -179,57 +179,51 @@ document.addEventListener('DOMContentLoaded', async () => { const btnImport = document.getElementById('btnImport'); btnImport.addEventListener('click', (e) => { e.preventDefault(); - importPrompts(); + importCustomDataPHs(); }); - function importPrompts() { - if(confirm(browser.i18n.getMessage("importPrompts_confirmText") + '\n' + browser.i18n.getMessage("customPrompts_managePrompts_info_default_2") + '\n' + browser.i18n.getMessage("customPrompts_managePrompts_info_default_3"))) { - //ask the user to choose a JSON file, and then read it, check if the serialized JSON is valid as generated from exportPrompts(), and if so, add it to the list + function importCustomDataPHs() { + if(confirm(browser.i18n.getMessage("importCustomDataPH_confirmText") + '\n' + browser.i18n.getMessage("customDataPH_manageDataPH_info_default") + '\n' + browser.i18n.getMessage("customPrompts_managePrompts_info_default_3"))) { + //ask the user to choose a JSON file, and then read it, check if the serialized JSON is valid as generated from importCustomDataPHs(), and if so, add it to the list const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.click(); input.onchange = async () => { - setMessage(browser.i18n.getMessage('customPrompts_start_import')); + setMessage(browser.i18n.getMessage('importCustomDataPH_start_import')); const file = input.files[0]; const reader = new FileReader(); reader.onload = async () => { const json = reader.result; try { const obj = JSON.parse(json); - if(obj.id !== 'thunderai-prompts') { - alert(browser.i18n.getMessage("importPrompts_invalidFile")); - setMessage(browser.i18n.getMessage('importPrompts_invalidFile'),'red'); + if(obj.id !== 'thunderai-custom-data-placeholders') { + alert(browser.i18n.getMessage("importCustomDataPH_invalidFile")); + setMessage(browser.i18n.getMessage('importCustomDataPH_invalidFile'),'red'); return; } // if(obj.addon_version !== manifest.version) { // alert(browser.i18n.getMessage("importPrompts_invalidVersion")); // return; // } - if(!Array.isArray(obj.prompts)) { - alert(browser.i18n.getMessage("importPrompts_invalidPrompts")); - setMessage(browser.i18n.getMessage('customPrompts_invalidPrompts'),'red'); + if(!Array.isArray(obj.customdataplaceholders)) { + alert(browser.i18n.getMessage("importCustomDataPH_invalidDataPHs")); + setMessage(browser.i18n.getMessage('importCustomDataPH_invalidDataPHs'),'red'); return; } - //setCustomPrompts(obj.prompts); customDataPHsList.clear(); - loadCustomDataPHsList(await preparePromptsForImport(obj.prompts)); + loadCustomDataPHsList(await prepareCustomDataPHsForImport(obj.customdataplaceholders)); setSomethingChanged(); i18n.updateDocument(); - // browser.runtime.sendMessage({command: "reload_menus"}); - setMessage(browser.i18n.getMessage('customPrompts_import_completed'), 'orange'); - // msgTimeout = setTimeout(() => { - // clearMessage(); - // }, 10000); + setMessage(browser.i18n.getMessage('importCustomDataPH_import_completed'), 'orange'); } catch(err) { - alert(browser.i18n.getMessage("importPrompts_invalidFile") + ' ' + err); - setMessage(browser.i18n.getMessage('importPrompts_invalidFile'),'red'); + alert(browser.i18n.getMessage("importCustomDataPH_invalidFile") + ' ' + err); + setMessage(browser.i18n.getMessage('importCustomDataPH_invalidFile'),'red'); return; } }; reader.readAsText(file); }; - }; } From 7471f7691ace85f4b7e31d21e75e499aff05a23d Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:42:00 +0200 Subject: [PATCH 11/15] Add support for custom placeholders in text replacement and validation. see #156 --- js/mzta-placeholders.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/js/mzta-placeholders.js b/js/mzta-placeholders.js index 1cec0dbf..f640e114 100644 --- a/js/mzta-placeholders.js +++ b/js/mzta-placeholders.js @@ -335,6 +335,19 @@ export const placeholdersUtils = { }); }, + async replaceCustomPlaceholders(text) { + let customPlaceholders = await getCustomPlaceholders(true); + // Regular expression to match patterns like {%thunderai_custom_...%} + return text.replace(/{%\s*thunderai_custom_(.*?)\s*%}/g, function(match, p1) { + // p1 contains the key inside {% %} + const currPlaceholder = customPlaceholders.find(ph => ph.id === 'thunderai_custom_' + p1); + if (!currPlaceholder) { + return match; + } + return currPlaceholder.text; + }); + }, + hasPlaceholder(text, placeholder = "") { let regex; @@ -349,9 +362,30 @@ export const placeholdersUtils = { // Check if the specific or any placeholder is present in the text return regex.test(text); - }, + }, + + hasCustomPlaceholder(text, placeholder = "") { + let regex; + + // If a specific placeholder is provided, we search for it + if (placeholder !== "") { + // Dynamically build the regex for the specific placeholder + regex = new RegExp(`{%\s*${placeholder}\s*%}`); + } else { + // Otherwise, we search for any placeholder in the format {%thunderai_custom_ ... %} + regex = /{%\s*thunderai_custom_(.*?)\s*%}/; + } + + // Check if the specific or any placeholder is present in the text + return regex.test(text); + }, async getPlaceholdersValues(prompt_text, curr_message, mail_subject, body_text, msg_text, only_typed_text, only_quoted_text, selection_text, selection_html, tags_full_list) { + // check if we have custom placeholders + if(placeholdersUtils.hasCustomPlaceholder(prompt_text)){ + prompt_text = await placeholdersUtils.replaceCustomPlaceholders(prompt_text); + } + let currPHs = await placeholdersUtils.extractPlaceholders(prompt_text); // console.log(">>>>>>>>>> currPHs: " + JSON.stringify(currPHs)); let finalSubs = {}; From afa773fe84abf45a6e0c0978faacc672b9f107a4 Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:47:00 +0200 Subject: [PATCH 12/15] handling custom placeholders in checking custom prompts features. see #156 --- pages/customprompts/mzta-custom-prompts.js | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pages/customprompts/mzta-custom-prompts.js b/pages/customprompts/mzta-custom-prompts.js index faecf19b..c4126ce4 100644 --- a/pages/customprompts/mzta-custom-prompts.js +++ b/pages/customprompts/mzta-custom-prompts.js @@ -20,7 +20,7 @@ import { prefs_default } from "../../options/mzta-options-default.js"; import { getPrompts, setDefaultPromptsProperties, setCustomPrompts, preparePromptsForExport, preparePromptsForImport } from "../../js/mzta-prompts.js"; import { ChatGPTWeb_models, isThunderbird128OrGreater, getLocalStorageUsedSpace, sanitizeHtml, validateCustomData_ChatGPTWeb, getChatGPTWebModelsList_HTML } from "../../js/mzta-utils.js"; import { taLogger } from "../../js/mzta-logger.js"; -import { getPlaceholders } from "../../js/mzta-placeholders.js"; +import { getPlaceholders, placeholdersUtils } from "../../js/mzta-placeholders.js"; import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js"; let prefs = null; @@ -88,13 +88,13 @@ document.addEventListener('DOMContentLoaded', async () => { const textareas = document.querySelectorAll('.editor'); autocompleteSuggestions = (await getPlaceholders(true)).map(p => ({command: '{%'+p.id+'%}', type: p.type})); - // console.log('>>>>>>>>>>> suggestions: ' + JSON.stringify(suggestions)); + // console.log('>>>>>>>>>>> autocompleteSuggestions: ' + JSON.stringify(autocompleteSuggestions)); textareas.forEach(textarea => { textareaAutocomplete(textarea, autocompleteSuggestions); textareas.forEach(textarea => { - textarea.addEventListener('input', (e) => { - checkPromptsConfigForPlaceholders(e.target); + textarea.addEventListener('input', async (e) => { + await checkPromptsConfigForPlaceholders(e.target); }); textareaAutocomplete(textarea, autocompleteSuggestions); }); @@ -161,7 +161,6 @@ document.addEventListener('DOMContentLoaded', async () => { } }); - //To add a new item var txtIdNew = document.getElementById('txtIdNew'); var txtNameNew = document.getElementById('txtNameNew'); @@ -588,13 +587,13 @@ function handleConfirmClick(e) { } // Handle checkbox changes and log new state -function handleCheckboxChange(e) { +async function handleCheckboxChange(e) { e.preventDefault(); e.target.setAttribute('checked_val', e.target.checked ? '1' : '0'); //console.log('>>>>>>>> checked_val: ' + e.target.getAttribute('checked_val')); if (e.target.classList.contains('need_selected') || e.target.classList.contains('need_custom_text') || e.target.classList.contains('need_selected_new') || e.target.classList.contains('need_custom_text_new')) { let textarea = e.target.closest('tr').querySelector('.text_output'); - checkPromptsConfigForPlaceholders(textarea); + await checkPromptsConfigForPlaceholders(textarea); } } @@ -950,11 +949,15 @@ if(await isThunderbird128OrGreater()){ }); } -function checkPromptsConfigForPlaceholders(textarea){ +async function checkPromptsConfigForPlaceholders(textarea){ + let curr_text = textarea.value; + // First substitute the custom data placeholders + curr_text = await placeholdersUtils.replaceCustomPlaceholders(curr_text); + console.log('>>>>>>>>>> curr_text after custom placeholders: ' + curr_text); // check additional_text and selected_text placeholders presence and the corrispondent checkboxes let tr_ancestor = textarea.closest('tr'); let need_custom_text_element = tr_ancestor.querySelector('.need_custom_text') || tr_ancestor.querySelector('.need_custom_text_new'); - if(String(textarea.value).indexOf('{%additional_text%}') != -1){ + if(String(curr_text).indexOf('{%additional_text%}') != -1){ if(!need_custom_text_element.checked){ need_custom_text_element.closest('.need_custom_text_span').style.border = '2px solid red'; }else{ @@ -966,7 +969,7 @@ function checkPromptsConfigForPlaceholders(textarea){ let tr_ancestor2 = textarea.closest('tr'); let selected_text_element = tr_ancestor2.querySelector('.need_selected') || tr_ancestor2.querySelector('.need_selected_new'); - if((String(textarea.value).indexOf('{%selected_text%}') != -1)||(String(textarea.value).indexOf('{%selected_html%}') != -1)){ + if((String(curr_text).indexOf('{%selected_text%}') != -1)||(String(curr_text).indexOf('{%selected_html%}') != -1)){ if(!selected_text_element.checked){ selected_text_element.closest('.need_selected_span').style.border = '2px solid red'; }else{ From 434edea30e6ab3b727013595df952db1d8e32373 Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:47:00 +0200 Subject: [PATCH 13/15] Remove unnecessary custom text checkboxes and related logic from custom data placeholders. see #156 --- .../mzta-custom-dataplaceholders.html | 7 --- .../mzta-custom-dataplaceholders.js | 58 ------------------- 2 files changed, 65 deletions(-) diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.html b/pages/customdataplaceholders/mzta-custom-dataplaceholders.html index ea95d2b4..0fe4414b 100644 --- a/pages/customdataplaceholders/mzta-custom-dataplaceholders.html +++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.html @@ -48,13 +48,6 @@ - - -
- -
- - diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.js b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js index cbfd37c0..f3d12cc5 100644 --- a/pages/customdataplaceholders/mzta-custom-dataplaceholders.js +++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.js @@ -117,8 +117,6 @@ document.addEventListener('DOMContentLoaded', async () => { name: txtNameNew.value.trim(), text: txtTextNew.value.trim(), type: selectTypeNew.value, - need_selected: (checkboxNeedSelectedNew.checked) ? 1 : 0, - need_custom_text: (checkboxNeedCustomTextNew.checked) ? 1 : 0, enabled: 1, is_default: 0, idnum: idnumMax + 1, @@ -253,8 +251,6 @@ function showItemRowEditor(tr) { tr.querySelector('.text_show').style.display = 'none'; tr.querySelector('.type_output').style.display = 'inline'; tr.querySelector('.type_show').style.display = 'none'; - tr.querySelector('input.need_selected').disabled = false; - tr.querySelector('input.need_custom_text').disabled = false; } function hideItemRowEditor(tr) { @@ -266,8 +262,6 @@ function hideItemRowEditor(tr) { tr.querySelector('.text_show').style.display = 'inline'; tr.querySelector('.type_output').style.display = 'none'; tr.querySelector('.type_show').style.display = 'inline'; - tr.querySelector('input.need_selected').disabled = true; - tr.querySelector('input.need_custom_text').disabled = true; } // Confirm and log deletion action @@ -317,18 +311,6 @@ function handleConfirmClick(e) { setSomethingChanged(); } -// 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')); - if (e.target.classList.contains('need_selected') || e.target.classList.contains('need_custom_text') || e.target.classList.contains('need_selected_new') || e.target.classList.contains('need_custom_text_new')) { - let textarea = e.target.closest('tr').querySelector('.text_output'); - checkPromptsConfigForPlaceholders(textarea); - } - -} - // Enable save button on input change function handleInputChange(e) { e.preventDefault(); @@ -348,8 +330,6 @@ function loadCustomDataPHsList(values){ 'name', 'text', 'type', - { name: 'need_selected', attr: 'checked_val'}, - { name: 'need_custom_text', attr: 'checked_val'}, { name: 'enabled', attr: 'checked_val'} ], item: function(values) { @@ -387,10 +367,6 @@ function loadCustomDataPHsList(values){ ` - -
- -
@@ -436,11 +412,6 @@ function loadCustomDataPHsList(values){ element.addEventListener('click', handleConfirmClick); }); - let checkbox_elements = document.querySelectorAll("input[type='checkbox']"); - checkbox_elements.forEach(element => { - element.addEventListener('change', handleCheckboxChange); - }); - document.querySelectorAll('.input_mod').forEach(element => { element.addEventListener('change', handleInputChange); }); @@ -524,8 +495,6 @@ function setNothingChanged(){ function checkSelectedBoxes(checkboxes = null) { if(checkboxes == null){ checkboxes = [ - ...document.querySelectorAll('.need_selected[type="checkbox"]'), - ...document.querySelectorAll('.need_custom_text[type="checkbox"]'), ...document.querySelectorAll('.enabled[type="checkbox"]'), ]; } @@ -592,31 +561,4 @@ if(await isThunderbird128OrGreater()){ event.preventDefault(); } }); -} - -function checkPromptsConfigForPlaceholders(textarea){ - // check additional_text and selected_text placeholders presence and the corrispondent checkboxes - let tr_ancestor = textarea.closest('tr'); - let need_custom_text_element = tr_ancestor.querySelector('.need_custom_text') || tr_ancestor.querySelector('.need_custom_text_new'); - if(String(textarea.value).indexOf('{%additional_text%}') != -1){ - if(!need_custom_text_element.checked){ - need_custom_text_element.closest('.need_custom_text_span').style.border = '2px solid red'; - }else{ - need_custom_text_element.closest('.need_custom_text_span').style.border = ''; - } - }else{ - need_custom_text_element.closest('.need_custom_text_span').style.border = ''; - } - - let tr_ancestor2 = textarea.closest('tr'); - let selected_text_element = tr_ancestor2.querySelector('.need_selected') || tr_ancestor2.querySelector('.need_selected_new'); - if((String(textarea.value).indexOf('{%selected_text%}') != -1)||(String(textarea.value).indexOf('{%selected_html%}') != -1)){ - if(!selected_text_element.checked){ - selected_text_element.closest('.need_selected_span').style.border = '2px solid red'; - }else{ - selected_text_element.closest('.need_selected_span').style.border = ''; - } - }else{ - selected_text_element.closest('.need_selected_span').style.border = ''; - } } \ No newline at end of file From 65e410f41c1f1daf0ad742b7708831aa73a5df6d Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:47:00 +0200 Subject: [PATCH 14/15] label fixed --- _locales/en/messages.json | 4 ++++ .../customdataplaceholders/mzta-custom-dataplaceholders.html | 2 +- pages/customdataplaceholders/mzta-custom-dataplaceholders.js | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 7cf15bfe..00f439b0 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1611,5 +1611,9 @@ "importCustomDataPH_invalidDataPHs": { "message": "The file you're trying to import does not contain any valid custom data placeholders.", "description": "" + }, + "customDataPH_add_to_menu": { + "message": "Usable in prompts added to menu", + "description": "" } } \ No newline at end of file diff --git a/pages/customdataplaceholders/mzta-custom-dataplaceholders.html b/pages/customdataplaceholders/mzta-custom-dataplaceholders.html index 0fe4414b..86a4263d 100644 --- a/pages/customdataplaceholders/mzta-custom-dataplaceholders.html +++ b/pages/customdataplaceholders/mzta-custom-dataplaceholders.html @@ -40,7 +40,7 @@
- +
From ca99b80dc8404b91fb2103e73b93375708bdc6a9 Mon Sep 17 00:00:00 2001 From: Mic Date: Sun, 15 Jun 2025 23:52:00 +0200 Subject: [PATCH 15/15] release notes updated --- CHANGELOG.md | 1 + options/mzta-release-notes.html | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d6c2ec0..4626fc93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@

Version 3.6.0 - ??/??/2025

    +
  • Now it's possibile to define custom data placeholders to be used in custom prompts [#156].
  • When replying, it's now possibile to choose a different reply type (between "all" or "sender only") directly in the AI chat window [#372].
  • [All APIs] Added an option to choose to exclude a tag only with an exact match in the excluded words list [#395].
  • [OpenAI API] Added an option to enable the OpenAI storage for API requests [#406].
  • diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index da606a40..ccda247d 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -9,6 +9,7 @@

    ThunderAI Release Notes

    Version 3.6.0 - ??/??/2025

      +
    • Now it's possibile to define custom data placeholders to be used in custom prompts [#156].
    • When replying, it's now possibile to choose a different reply type (between "all" or "sender only") directly in the AI chat window [#372].
    • [All APIs] Added an option to choose to exclude a tag only with an exact match in the excluded words list [#395].
    • [OpenAI API] Added an option to enable the OpenAI storage for API requests [#406].