y evitar cualquier problema de CORS."
+ },
+ "CORS_give_allurls_perm": {
+ "message": "Otorgar permiso “all URLs”"
+ },
+ "prefs_OptionText_composing_plain_text": {
+ "message": "Redactando en texto plano"
+ },
+ "prefs_OptionText_composing_plain_text_Info": {
+ "message": "Marca esta opción si estás redactando los correos electrónicos en formato de texto plano."
+ },
+ "Replace_No_Selected_Text": {
+ "message": "¿Ningún texto seleccionado, deseas insertar la respuesta de la IA al inicio del correo electrónico?"
+ },
+ "prefs_ollama_num_ctx": {
+ "message": "Número de tokens de contexto"
+ },
+ "prefs_ollama_num_ctx_Info": {
+ "message": "El número de tokens de contexto que se usarán para la API de Ollama. Establece 0 si no deseas pasarlo como parámetro al servidor."
+ },
+ "ask_chatgptweb_permission_1": {
+ "message": "Para usar la integración de ChatGPT Web, necesitas otorgar el permiso requerido."
+ },
+ "ask_anthropic_api_permission_1": {
+ "message": "Para usar la integración de la API de Claude, necesitas otorgar el permiso requerido."
+ },
+ "ask_openai_api_permission_1": {
+ "message": "Para usar la integración de la API de OpenAI, necesitas otorgar el permiso requerido."
+ },
+ "ask_integration_permission_2_popup": {
+ "message": "Haz clic aquí para abrir una nueva pestaña y seguir las instrucciones."
+ },
+ "ask_integration_permission_2": {
+ "message": "Haz clic aquí para continuar."
+ },
+ "ask_integration_permission_ok": {
+ "message": "Permiso concedido. Puedes hacer clic aquí para cerrar esta pestaña y volver a la ventana principal."
+ },
+ "AccountSelector_AutoTags": {
+ "message": "Elige las cuentas donde el etiquetado automático está habilitado"
+ },
+ "AccountSelector_AutoTags_infoline": {
+ "message": "Cada cambio se guarda inmediatamente."
+ },
+ "AccountSelector_Spamfilter": {
+ "message": "Elige las cuentas donde el filtro de spam está habilitado"
+ },
+ "prefs_OptionText_chatgpt_web_project": {
+ "message": "Proyecto ChatGPT Web"
+ },
+ "prefs_OptionText_chatgpt_web_project_info": {
+ "message": "Este es el proyecto que se aplicará para la interfaz web de ChatGPT."
+ },
+ "prefs_OptionText_chatgpt_web_custom_gpt": {
+ "message": "ChatGPT Web GPT personalizado"
+ },
+ "prefs_OptionText_chatgpt_web_custom_gpt_info": {
+ "message": "Este es el GPT personalizado que se aplicará para la interfaz web de ChatGPT."
+ },
+ "prefs_OptionText_chatgpt_web_custom_data_info": {
+ "message": "Esto debe estar en el siguiente formato:"
+ },
+ "prefs_OptionText_chatgpt_web_custom_data_info2": {
+ "message": "Puedes encontrar el valor correcto en el campo de URL del navegador al abrir la página de ChatGPT correspondiente."
+ },
+ "customPrompts_Properties": {
+ "message": "Propiedades"
+ },
+ "customPrompts_show_additional_info": {
+ "message": "Mostrar propiedades adicionales"
+ },
+ "customPrompts_hide_additional_info": {
+ "message": "Ocultar propiedades adicionales"
+ },
+ "customPrompts_show_additional_info_show": {
+ "message": "Propiedades adicionales"
+ },
+ "prefs_OptionText_CustomGPT_Warn": {
+ "message": "Si se especifica un proyecto en las opciones o en un prompt, sobrescribirá la configuración del GPT personalizado."
+ },
+ "prefs_OptionText_Project_No_temporary_chat_warn": {
+ "message": "Si se especifica un proyecto en las opciones o en un prompt, no se usará el chat temporal."
+ },
+ "prefs_Anthropic_API_Key": {
+ "message": "Clave API de Claude"
+ },
+ "prefs_Connection_type_Anthropic_API": {
+ "message": "API de Claude"
+ },
+ "Anthropic_Models": {
+ "message": "Modelos Claude"
+ },
+ "Anthropic_Models_Fetch": {
+ "message": "Actualizar lista de modelos de Claude"
+ },
+ "Anthropic_Models_Error_fetching": {
+ "message": "Error al intentar obtener los modelos de Claude"
+ },
+ "Anthropic_Version": {
+ "message": "Versión de la API de Claude"
+ },
+ "Anthropic_Version_Info": {
+ "message": "REQUERIDO. No cambies este valor a menos que sepas lo que estás haciendo. Más información en:"
+ },
+ "prefs_OptionText_anthropic_max_tokens": {
+ "message": "Tokens máximos"
+ },
+ "prefs_OptionText_anthropic_max_tokens_Info": {
+ "message": "El número máximo de tokens a generar en la completación. El recuento de tokens de tu prompt más max_tokens no puede exceder la longitud del contexto del modelo."
+ },
+ "anthropic_empty_apikey": {
+ "message": "No has añadido una clave API para la API de Claude. Por favor, inserta una en la página de opciones."
+ },
+ "anthropic_empty_model": {
+ "message": "No has seleccionado un modelo para la API de Claude. Por favor, elige uno en la página de opciones."
+ },
+ "anthropic_empty_version": {
+ "message": "No has añadido una cadena de versión para la API de Claude. Por favor, insértala en la página de opciones."
+ },
+ "anthropic_api_request_failed": {
+ "message": "Solicitud a la API de Claude falló"
+ },
+ "_api_connecting": {
+ "message": "Intentando conectar a $api_string$ usando la siguiente configuración...",
+ "placeholders": {
+ "api_string": {
+ "content": "$1"
+ }
+ }
+ },
+ "_api_connecting_model": {
+ "message": "Modelo"
+ },
+ "_api_connecting_host": {
+ "message": "Host"
+ },
+ "_api_connecting_version": {
+ "message": "Versión"
+ },
+ "prefs_OpenAIComp_AvailableServices": {
+ "message": "Servicios disponibles"
+ },
+ "prefs_OpenAIComp_AvailableServices_Info": {
+ "message": "Elige uno de los servicios disponibles para la API compatible con OpenAI o insértalo manualmente."
+ },
+ "Custom": {
+ "message": "Personalizado"
+ },
+ "OpenAIComp_Configs_ConfirmApply": {
+ "message": "¿Estás seguro de que deseas aplicar la configuración \"$config_name$\"?",
+ "placeholders": {
+ "config_name": {
+ "content": "$1"
+ }
+ }
+ },
+ "apiwebchat_selection_info": {
+ "message": "Si seleccionas texto, solo esa parte será considerada."
+ },
+ "ChatGPT_chatgpt_api_store": {
+ "message": "Usar almacenamiento"
+ },
+ "ChatGPT_chatgpt_api_store_info": {
+ "message": "Si está marcado, tus chats serán almacenados por OpenAI."
+ },
+ "prefs_chatgpt_api_temperature_Info": {
+ "message": "Qué temperatura de muestreo usar, entre 0 y 2. Valores más altos como 0.8 harán que la salida sea más aleatoria, mientras que valores más bajos como 0.2 la harán más enfocada y determinista."
+ },
+ "prefs_ollama_temperature_Info": {
+ "message": "La temperatura del modelo. Incrementar la temperatura hará que el modelo responda de forma más creativa. El valor predeterminado es 0.8. Se recomienda usar valores entre 0 y 1."
+ },
+ "prefs_ollama_think": {
+ "message": "Activar el pensamiento"
+ },
+ "prefs_ollama_think_Info": {
+ "message": "Si está marcado, el modelo pensará antes de responder. Esta opción solo funciona con modelos que admiten la función «think»."
+ },
+ "chatgpt_win_change_reply_type": {
+ "message": "Haz clic para cambiar el tipo de respuesta"
+ },
+ "prefs_OptionText_add_tags_exclusions_exact_match": {
+ "message": "Coincidencia exacta de exclusiones"
+ },
+ "prefs_OptionText_add_tags_exclusions_exact_match_Info": {
+ "message": "Si está marcado, las palabras de la lista de exclusión se compararán exactamente con las etiquetas. De lo contrario, también coincidirán si están incluidas dentro de la etiqueta."
+ },
+ "customDataPH_manageDataPH": {
+ "message": "Administrar marcadores de datos"
+ },
+ "customDataPH_manageDataPH_info_default_3": {
+ "message": "También puedes usar los marcadores de datos predeterminados con autocompletar, como cuando se escriben prompts personalizados."
+ },
+ "customDataPH_manageDataPH_info_default": {
+ "message": "En esta página, es posible definir marcadores de datos personalizados para usarlos en tus prompts personalizados."
+ },
+ "customDataPH_manageDataPH_info_default_2": {
+ "message": "Los marcadores de datos existentes con el mismo ID se sobrescribirán. Los marcadores con IDs nuevos se añadirán."
+ },
+ "customDataPH_ExportAll": {
+ "message": "Exportar todos los marcadores de datos personalizados"
+ },
+ "customDataPH_Import": {
+ "message": "Importar nuevos marcadores de datos"
+ },
+ "customDataPH_form_label_Text": {
+ "message": "Texto del marcador de datos"
+ },
+ "customDataPH_saving_custom": {
+ "message": "Guardando marcadores de datos personalizados..."
+ },
+ "customDataPH_saved": {
+ "message": "¡Marcadores de datos personalizados guardados!"
+ },
+ "customDataPH_btnAddNewCommit": {
+ "message": "Agregar el marcador de datos"
+ },
+ "importCustomDataPH_confirmText": {
+ "message": "Estás a punto de importar nuevos marcadores de datos personalizados."
+ },
+ "importCustomDataPH_start_import": {
+ "message": "Iniciando la importación de marcadores de datos personalizados..."
+ },
+ "importCustomDataPH_import_completed": {
+ "message": "¡Importación de marcadores de datos personalizados completada! Necesitas hacer clic en el botón 'Guardar todo' para guardar tus cambios."
+ },
+ "importCustomDataPH_invalidFile": {
+ "message": "El archivo que estás intentando importar no es un archivo válido de marcadores de datos personalizados."
+ },
+ "importCustomDataPH_invalidDataPHs": {
+ "message": "El archivo que estás intentando importar no contiene ningún marcador de datos personalizado válido."
+ },
+ "customDataPH_add_to_menu": {
+ "message": "Utilizable en los prompts añadidos al menú"
+ },
+ "prefs_OptionText_chatgpt_web_br_replace_info": {
+ "message": "Tenga en cuenta que cualquier etiqueta en la respuesta de la IA será reemplazada por saltos de línea."
+ },
+ "prefs_OpenAIComp_ClearModelsList": {
+ "message": "Borrar lista de modelos"
+ },
+ "OpenAIComp_ClearModelsList_Confirm": {
+ "message": "¿Estás seguro de que deseas borrar la lista de modelos? Esta acción no se puede deshacer."
+ },
+ "prefs_api_temperature": {
+ "message": "Temperatura"
+ },
+ "prefs_openai_comp_temperature_Info": {
+ "message": "Qué temperatura de muestreo usar, entre 0 y 2. Valores más altos como 0.8 harán que la salida sea más aleatoria, mientras que valores más bajos como 0.2 la harán más enfocada y determinista."
+ },
+ "chatgpt_click_force_completion": {
+ "message": "Parece que no es posible saber si ChatGPT ha terminado. Haz clic aquí para forzar la finalización del trabajo."
+ },
+ "warn_API_needed": {
+ "message": "Para usar esta función, necesitas una integración de API en lugar de la integración de ChatGPT Web. Puedes definir una API específica en la página de configuración de la función marcando primero la casilla de verificación arriba y luego haciendo clic en el botón de la izquierda."
+ },
+ "prefs_google_gemini_thinking_budget": {
+ "message": "Presupuesto de pensamiento"
+ },
+ "prefs_google_gemini_thinking_budget_Info": {
+ "message": "Define el número de tokens a usar para pensar. Deja este campo vacío si el modelo seleccionado no admite el pensamiento o si deseas usar el método predeterminado. Introduce 0 para desactivar el pensamiento, o -1 para habilitar el pensamiento dinámico."
+ },
+ "prefs_google_gemini_temperature_Info": {
+ "message": "Este parámetro debe ser un número entre 0.0 y 2.0. Controla la aleatoriedad de la salida. El valor predeterminado varía según el modelo. Déjalo vacío para evitar establecer el parámetro en la llamada a la API."
+ },
+ "SelectAll": {
+ "message": "Seleccionar todo"
+ },
+ "DeselectAll": {
+ "message": "Deseleccionar todo"
+ },
+ "Optional_Permission_Denied_Model_Fetching": {
+ "message": "Has denegado el permiso opcional necesario para obtener los modelos de esta integración."
+ },
+ "prefs_anthropic_temperature_Info": {
+ "message": "Cantidad de aleatoriedad inyectada en la respuesta. Por defecto es 1.0. Varía de 0.0 a 1.0. Usa una temperatura más cercana a 0.0 para tareas analíticas / de opción múltiple, y más cercana a 1.0 para tareas creativas y generativas. Ten en cuenta que incluso con una temperatura de 0.0, los resultados no serán completamente determinísticos."
+ },
+ "Anthropic_System_Prompt_Info": {
+ "message": "Puedes mejorar el rendimiento de Claude usando un Prompt del Sistema para asignarle un rol. Esta técnica, conocida como prompting de rol, es la forma más poderosa de usar prompts del sistema con Claude. El rol adecuado puede convertir a Claude de un asistente general en tu experto virtual en el dominio."
+ },
+ "Anthropic_System_Prompt": {
+ "message": "Prompt del sistema"
}
}
diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json
index 140dc7e1..89cc8a12 100644
--- a/_locales/fr/messages.json
+++ b/_locales/fr/messages.json
@@ -1107,7 +1107,7 @@
"message": "Version de l’API Claude"
},
"prefs_OptionText_anthropic_max_tokens": {
- "message": "Nombre maximum de jetons Claude"
+ "message": "Nombre maximum de jetons"
},
"anthropic_api_request_failed": {
"message": "Échec de la requête à l'API Claude"
@@ -1312,12 +1312,28 @@
"Optional_Permission_Denied_Model_Fetching": {
"message": "Vous avez refusé l’autorisation facultative nécessaire pour récupérer les modèles pour cette intégration."
},
- "prefs_OptionText_auto_summary": {
- "message": "Activer la synthèse automatique par IA pour les aperçus de messages",
- "description": ""
+ "prompt_string": {
+ "message": "Prompt"
},
- "prefs_OptionText_auto_summary_Info": {
- "message": "Si cette option est cochée, ThunderAI générera et affichera automatiquement des synthèses par IA au-dessus des messages électroniques lorsqu'ils sont ouverts. Notez que cela signifie que tous les messages que vous prévisualisez seront immédiatement envoyés au service IA configuré.",
- "description": ""
+ "placeholder_mail_headers": {
+ "message": "En-têtes d'e-mail"
+ },
+ "prefs_chatgpt_api_temperature_Info": {
+ "message": "Quelle température d'échantillonnage utiliser, entre 0 et 2. Des valeurs plus élevées comme 0,8 rendront le résultat plus aléatoire, tandis que des valeurs plus basses comme 0,2 le rendront plus ciblé et déterministe."
+ },
+ "prefs_ollama_temperature_Info": {
+ "message": "La température du modèle. Augmenter la température rendra les réponses du modèle plus créatives. La valeur par défaut est 0,8. Il est recommandé d'utiliser des valeurs comprises entre 0 et 1."
+ },
+ "prefs_api_temperature": {
+ "message": "Température"
+ },
+ "prefs_openai_comp_temperature_Info": {
+ "message": "Quelle température d'échantillonnage utiliser, entre 0 et 2. Des valeurs plus élevées, comme 0,8, rendront le résultat plus aléatoire, tandis que des valeurs plus basses, comme 0,2, le rendront plus ciblé et déterministe."
+ },
+ "prefs_google_gemini_temperature_Info": {
+ "message": "Ce paramètre doit être un nombre compris entre 0,0 et 2,0. Il contrôle le caractère aléatoire du résultat. La valeur par défaut varie selon le modèle. Laissez ce champ vide pour ne pas définir le paramètre dans l'appel d'API."
+ },
+ "prefs_anthropic_temperature_Info": {
+ "message": "Degré d'aléa injecté dans la réponse. La valeur par défaut est 1,0. La plage de valeurs s'étend di 0,0 à 1,0. Utilisez une température proche de 0,0 pour des tâches analytiques ou des choix multiples, et proche de 1,0 pour des tâches créatives et génératives. Notez que même avec une température de 0,0, les résultats ne seront pas totalement déterministes."
}
}
diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json
new file mode 100644
index 00000000..501458e0
--- /dev/null
+++ b/_locales/sv/messages.json
@@ -0,0 +1,71 @@
+{
+ "extensionDescription": {
+ "message": "Använd ChatGPT, Google Gemini, Claude eller Ollama för att förbättra dina e-postmeddelanden!"
+ },
+ "menu_title": {
+ "message": "AI"
+ },
+ "customPrompts_btnEdit": {
+ "message": "Redigera"
+ },
+ "customPrompts_btnCancel": {
+ "message": "Avbryt"
+ },
+ "customPrompts_btnOK": {
+ "message": "Ok"
+ },
+ "customPrompts_btnDelete": {
+ "message": "Ta bort"
+ },
+ "customPrompts_btnDelete_confirmText": {
+ "message": "Är du säker på att du vill ta bort det här objektet?"
+ },
+ "customPrompts_unsaved_changes": {
+ "message": "Det finns osparade ändringar!"
+ },
+ "btnSaveAll_string": {
+ "message": "Spara alla"
+ },
+ "chatgpt_win_close": {
+ "message": "Stäng"
+ },
+ "customPrompts_add_to_menu_always": {
+ "message": "Alltid"
+ },
+ "Date": {
+ "message": "Datum"
+ },
+ "prompt_selection_needed": {
+ "message": "För att fortsätta måste du markera lite text!"
+ },
+ "customPrompts_form_label_Name": {
+ "message": "Namn"
+ },
+ "customPrompts_form_label_Action": {
+ "message": "Åtgärd"
+ },
+ "customPrompts_form_label_enabled": {
+ "message": "Aktiverad"
+ },
+ "btnNew_string": {
+ "message": "Lägg till ny"
+ },
+ "chatgpt_btn_retry": {
+ "message": "Försöka igen"
+ },
+ "placeholder_selected_html": {
+ "message": "Vald HTML"
+ },
+ "placeholder_additional_text": {
+ "message": "Ytterligare text"
+ },
+ "chatgpt_win_send": {
+ "message": "Skicka"
+ },
+ "prefs_OptionText_chatgpt_win_height": {
+ "message": "Höjd"
+ },
+ "prefs_OptionText_chatgpt_win_width": {
+ "message": "Bredd"
+ }
+}
diff --git a/api_webchat/controller.js b/api_webchat/controller.js
index d4a9cc52..050471d2 100644
--- a/api_webchat/controller.js
+++ b/api_webchat/controller.js
@@ -20,7 +20,7 @@
* The original code has been released under the Apache License, Version 2.0.
*/
-import { prefs_default } from '../options/mzta-options-default.js';
+import { prefs_default, integration_options_config } from '../options/mzta-options-default.js';
import { placeholdersUtils } from '../js/mzta-placeholders.js';
import { getAPIsInitMessageString, convertNewlinesToBr } from '../js/mzta-utils.js';
@@ -46,224 +46,161 @@ const messagesArea = document.querySelector('messages-area');
// The controller wires up all the components and workers together,
// managing the dependencies. A kind of "DI" class.
let worker = null;
+const integration = llm.replace('_api', '');
+const worker_path_map = {
+ chatgpt: '../js/workers/model-worker-openai_responses.js',
+ google_gemini: '../js/workers/model-worker-google_gemini.js',
+ ollama: '../js/workers/model-worker-ollama.js',
+ openai_comp: '../js/workers/model-worker-openai_comp.js',
+ anthropic: '../js/workers/model-worker-anthropic.js',
+};
-switch (llm) {
- case "chatgpt_api":
- worker = new Worker('../js/workers/model-worker-openai.js', { type: 'module' });
- break;
- case "google_gemini_api":
- worker = new Worker('../js/workers/model-worker-google_gemini.js', { type: 'module' });
- break;
- case "ollama_api":
- worker = new Worker('../js/workers/model-worker-ollama.js', { type: 'module' });
- break;
- case "openai_comp_api":
- worker = new Worker('../js/workers/model-worker-openai_comp.js', { type: 'module' });
- break;
- case "anthropic_api":
- worker = new Worker('../js/workers/model-worker-anthropic.js', { type: 'module' });
- break;
- default:
- console.error('[ThunderAI] API WebChat Unknown LLM type:', llm);
- break;
+const worker_path = worker_path_map[integration];
+
+if (worker_path) {
+ worker = new Worker(worker_path, { type: 'module' });
+} else {
+ console.error('[ThunderAI] API WebChat Unknown LLM type:', llm);
}
-messagesArea.init(worker);
+if (worker) {
+ messagesArea.init(worker);
+ messageInput.init(worker);
+ messageInput.setMessagesArea(messagesArea);
-// Initialize the messageInput component and pass the worker to it
-messageInput.init(worker);
-messageInput.setMessagesArea(messagesArea);
+ if (integration_options_config[integration]) {
+ const integration_prefix = integration;
+ const options_config = integration_options_config[integration];
+
+ let prefsToGet = { do_debug: prefs_default.do_debug };
+ for (const key in options_config) {
+ prefsToGet[`${integration_prefix}_${key}`] = prefs_default[`${integration_prefix}_${key}`];
+ }
+ if (integration === 'openai_comp') {
+ prefsToGet.openai_comp_chat_name = prefs_default.openai_comp_chat_name;
+ }
+
+ let prefs_api = await browser.storage.sync.get(prefsToGet);
-switch (llm) {
- case "chatgpt_api": {
- let prefs_api = await browser.storage.sync.get({
- chatgpt_api_key: prefs_default.chatgpt_api_key,
- chatgpt_model: prefs_default.chatgpt_model,
- chatgpt_developer_messages: prefs_default.chatgpt_developer_messages,
- chatgpt_api_store: prefs_default.chatgpt_api_store,
- do_debug: prefs_default.do_debug,
- });
let i18nStrings = {};
- i18nStrings["chatgpt_api_request_failed"] = browser.i18n.getMessage('chatgpt_api_request_failed');
+ const i18n_msg_key = integration === 'openai_comp' ? 'OpenAIComp_api_request_failed' : `${integration}_api_request_failed`;
+ i18nStrings[i18n_msg_key] = browser.i18n.getMessage(i18n_msg_key);
i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted');
- messageInput.setModel(prefs_api.chatgpt_model);
- messagesArea.setLLMName("ChatGPT");
- worker.postMessage({
- type: 'init',
- chatgpt_api_key: prefs_api.chatgpt_api_key,
- chatgpt_model: prefs_api.chatgpt_model,
- chatgpt_developer_messages: prefs_api.chatgpt_developer_messages,
- chatgpt_api_store: prefs_api.chatgpt_api_store,
- do_debug: prefs_api.do_debug,
- i18nStrings: i18nStrings,
- });
- let additional_text_elements = [];
- additional_text_elements.push({label: 'OpenAI Store', value: (prefs_api.chatgpt_api_store ? 'Yes' : 'No')});
- if(prefs_api.chatgpt_developer_messages && prefs_api.chatgpt_developer_messages.length > 0) {
- additional_text_elements.push({label: browser.i18n.getMessage("ChatGPT_Developer_Messages"), value: prefs_api.chatgpt_developer_messages});
+
+ messageInput.setModel(prefs_api[`${integration_prefix}_model`]);
+
+ let llmName = "API";
+ switch(integration) {
+ case 'chatgpt': llmName = "ChatGPT"; break;
+ case 'google_gemini': llmName = "Google Gemini"; break;
+ case 'ollama': llmName = "Ollama Local"; break;
+ case 'openai_comp': llmName = prefs_api.openai_comp_chat_name || "OpenAI Comp"; break;
+ case 'anthropic': llmName = "Claude"; break;
}
- additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)});
- messagesArea.appendUserMessage(getAPIsInitMessageString({
- api_string: "ChatGPT API",
- model_string: prefs_api.chatgpt_model,
- additional_messages: additional_text_elements
- }), "info");
- browser.runtime.sendMessage({
- command: "openai_api_ready_" + call_id,
- window_id: (await browser.windows.getCurrent()).id
- });
- break;
- }
- case "google_gemini_api": {
- let prefs_api = await browser.storage.sync.get({
- google_gemini_api_key: prefs_default.google_gemini_api_key,
- google_gemini_model: prefs_default.google_gemini_model,
- google_gemini_system_instruction: prefs_default.google_gemini_system_instruction,
- google_gemini_thinking_budget: prefs_default.google_gemini_thinking_budget,
- do_debug: prefs_default.do_debug,
- });
- let i18nStrings = {};
- i18nStrings["google_gemini_api_request_failed"] = browser.i18n.getMessage('google_gemini_api_request_failed');
- i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted');
- messageInput.setModel(prefs_api.google_gemini_model);
- messagesArea.setLLMName("Google Gemini");
- let additional_text_elements = [];
- if(prefs_api.google_gemini_system_instruction && prefs_api.google_gemini_system_instruction.length > 0) {
- additional_text_elements.push({label: browser.i18n.getMessage("GoogleGemini_SystemInstruction"), value: prefs_api.google_gemini_system_instruction});
+ messagesArea.setLLMName(llmName);
+
+ let workerInitMessage = {
+ type: 'init',
+ do_debug: prefs_api.do_debug,
+ i18nStrings: i18nStrings,
+ };
+
+ for (const key in options_config) {
+ const prefKey = `${integration_prefix}_${key}`;
+ workerInitMessage[prefKey] = prefs_api[prefKey];
}
- additional_text_elements.push({label: 'Thinking Budget', value: prefs_api.google_gemini_thinking_budget});
- additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)});
- worker.postMessage({
- type: 'init',
- google_gemini_api_key: prefs_api.google_gemini_api_key,
- google_gemini_model: prefs_api.google_gemini_model,
- google_gemini_system_instruction: prefs_api.google_gemini_system_instruction,
- google_gemini_thinking_budget: prefs_api.google_gemini_thinking_budget,
- do_debug: prefs_api.do_debug,
- i18nStrings: i18nStrings,
- });
- messagesArea.appendUserMessage(getAPIsInitMessageString({
- api_string: "Google Gemini API",
- model_string: prefs_api.google_gemini_model,
- additional_messages: additional_text_elements
- }), "info");
- browser.runtime.sendMessage({
- command: "google_gemini_api_ready_" + call_id,
- window_id: (await browser.windows.getCurrent()).id
- });
- break;
- }
- case "ollama_api": {
- let prefs_api = await browser.storage.sync.get({
- ollama_host: prefs_default.ollama_host,
- ollama_model: prefs_default.ollama_model,
- ollama_num_ctx: prefs_default.ollama_num_ctx,
- ollama_think: prefs_default.ollama_think,
- do_debug: prefs_default.do_debug,
- });
- let i18nStrings = {};
- i18nStrings["ollama_api_request_failed"] = browser.i18n.getMessage('ollama_api_request_failed');
- i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted');
- messageInput.setModel(prefs_api.ollama_model);
- messagesArea.setLLMName("Ollama Local");
- worker.postMessage({
- type: 'init',
- ollama_host: prefs_api.ollama_host,
- ollama_model: prefs_api.ollama_model,
- ollama_num_ctx: prefs_api.ollama_num_ctx,
- ollama_think: prefs_api.ollama_think,
- do_debug: prefs_api.do_debug,
- i18nStrings: i18nStrings
- });
- browser.runtime.sendMessage({
- command: "ollama_api_ready_" + call_id,
- window_id: (await browser.windows.getCurrent()).id
- });
+
+ worker.postMessage(workerInitMessage);
+
+ const additional_messages_config = {
+ chatgpt: [
+ { key: 'store', labelKey: 'ChatGPT_chatgpt_api_store', type: 'boolean' },
+ { key: 'developer_messages', labelKey: 'ChatGPT_Developer_Messages', type: 'string' },
+ { key: 'temperature', labelKey: 'prefs_api_temperature', type: 'string' }
+ ],
+ google_gemini: [
+ { key: 'system_instruction', labelKey: 'GoogleGemini_SystemInstruction', type: 'string' },
+ { key: 'temperature', labelKey: 'prefs_api_temperature', type: 'string' },
+ { key: 'thinking_budget', labelKey: 'prefs_google_gemini_thinking_budget', type: 'string' }
+ ],
+ ollama: [
+ { key: 'think', labelKey: 'prefs_ollama_think', type: 'boolean' },
+ { key: 'temperature', labelKey: 'prefs_api_temperature', type: 'string' },
+ { key: 'num_ctx', labelKey: 'prefs_ollama_num_ctx', type: 'number_gt_zero' }
+ ],
+ openai_comp: [
+ { key: 'temperature', labelKey: 'prefs_api_temperature', type: 'string' }
+ ],
+ anthropic: [
+ { key: 'system_prompt', labelKey: 'Anthropic_System_Prompt', type: 'string' },
+ { key: 'max_tokens', labelKey: 'prefs_OptionText_anthropic_max_tokens', type: 'number_gt_zero' },
+ { key: 'temperature', labelKey: 'prefs_api_temperature', type: 'string' }
+ ]
+ };
+
+ const getAdditionalMessages = (integration, prefs) => {
+ const messages = [];
+ const config = additional_messages_config[integration];
+ if (!config) return messages;
+
+ for (const item of config) {
+ const prefKey = `${integration}_${item.key}`;
+ const value = prefs[prefKey];
+
+ if (value !== undefined && value !== null && value !== '') {
+ let displayValue;
+ let shouldAdd = false;
+
+ switch (item.type) {
+ case 'boolean':
+ displayValue = value ? 'Yes' : 'No';
+ shouldAdd = true;
+ break;
+ case 'string':
+ if (value.length > 0) {
+ displayValue = value;
+ shouldAdd = true;
+ }
+ break;
+ case 'number_gt_zero':
+ if (value > 0) {
+ displayValue = value;
+ shouldAdd = true;
+ }
+ break;
+ }
+ if (shouldAdd) {
+ messages.push({ label: browser.i18n.getMessage(item.labelKey), value: displayValue });
+ }
+ }
+ }
+ return messages;
+ };
+
let additional_text_elements = [];
- additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)});
+ additional_text_elements.push({label: browser.i18n.getMessage("prompt_string"), value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)});
+ additional_text_elements.push(...getAdditionalMessages(integration, prefs_api));
+
+ const api_strings = {
+ chatgpt: "ChatGPT API",
+ google_gemini: "Google Gemini API",
+ ollama: "Ollama API",
+ openai_comp: "OpenAI Compatible API",
+ anthropic: "Claude API"
+ };
+
messagesArea.appendUserMessage(getAPIsInitMessageString({
- api_string: "Ollama API",
- model_string: prefs_api.ollama_model,
- host_string: prefs_api.ollama_host,
- additional_messages: additional_text_elements
- }), "info");
- break;
- }
- case "openai_comp_api": {
- let prefs_api = await browser.storage.sync.get({
- openai_comp_host: prefs_default.openai_comp_host,
- openai_comp_model: prefs_default.openai_comp_model,
- openai_comp_api_key: prefs_default.openai_comp_api_key,
- openai_comp_use_v1: prefs_default.openai_comp_use_v1,
- openai_comp_chat_name: prefs_default.openai_comp_chat_name,
- do_debug: prefs_default.do_debug,
- });
- let i18nStrings = {};
- i18nStrings["OpenAIComp_api_request_failed"] = browser.i18n.getMessage('OpenAIComp_api_request_failed');
- i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted');
- messageInput.setModel(prefs_api.openai_comp_model);
- messagesArea.setLLMName(prefs_api.openai_comp_chat_name);
- worker.postMessage({
- type: 'init',
- openai_comp_host: prefs_api.openai_comp_host,
- openai_comp_model: prefs_api.openai_comp_model,
- openai_comp_api_key: prefs_api.openai_comp_api_key,
- openai_comp_use_v1: prefs_api.openai_comp_use_v1,
- do_debug: prefs_api.do_debug,
- i18nStrings: i18nStrings,
- });
- let additional_text_elements = [];
- additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)});
- messagesArea.appendUserMessage(getAPIsInitMessageString({
- api_string: "OpenAI Compatible API",
- model_string: prefs_api.openai_comp_model,
- host_string: prefs_api.openai_comp_host,
+ api_string: api_strings[integration],
+ model_string: prefs_api[`${integration_prefix}_model`],
+ host_string: prefs_api[`${integration_prefix}_host`],
+ version_string: prefs_api[`${integration_prefix}_version`],
additional_messages: additional_text_elements
}), "info");
+
browser.runtime.sendMessage({
- command: "openai_comp_api_ready_" + call_id,
+ command: `${llm}_ready_${call_id}`,
window_id: (await browser.windows.getCurrent()).id
});
- break;
- }
- case "anthropic_api": {
- let prefs_api = await browser.storage.sync.get({
- anthropic_api_key: prefs_default.anthropic_api_key,
- anthropic_model: prefs_default.anthropic_model,
- anthropic_system_prompt: prefs_default.anthropic_system_prompt,
- anthropic_version: prefs_default.anthropic_version,
- anthropic_max_tokens: prefs_default.anthropic_max_tokens,
- do_debug: prefs_default.do_debug,
- });
- let i18nStrings = {};
- i18nStrings["anthropic_api_request_failed"] = browser.i18n.getMessage('anthropic_api_request_failed');
- i18nStrings["error_connection_interrupted"] = browser.i18n.getMessage('error_connection_interrupted');
- messageInput.setModel(prefs_api.anthropic_model);
- messagesArea.setLLMName("Claude");
- worker.postMessage({
- type: 'init',
- anthropic_api_key: prefs_api.anthropic_api_key,
- anthropic_model: prefs_api.anthropic_model,
- anthropic_system_prompt: prefs_api.anthropic_system_prompt,
- anthropic_version: prefs_api.anthropic_version,
- anthropic_max_tokens: prefs_api.anthropic_max_tokens,
- do_debug: prefs_api.do_debug,
- i18nStrings: i18nStrings,
- });
- let additional_text_elements = [];
- additional_text_elements.push({label: "Prompt", value: '[' + prompt_id + '] ' + decodeURIComponent(prompt_name)});
- additional_text_elements.push({label: "System Prompt", value: prefs_api.anthropic_system_prompt});
- messagesArea.appendUserMessage(getAPIsInitMessageString({
- api_string: "Claude API",
- model_string: prefs_api.anthropic_model,
- version_string: prefs_api.anthropic_version,
- additional_messages: additional_text_elements
- }), "info");
- browser.runtime.sendMessage({
- command: "anthropic_api_ready_" + call_id,
- window_id: (await browser.windows.getCurrent()).id
- });
- break;
}
}
diff --git a/js/api/anthropic.js b/js/api/anthropic.js
index e1bb27a5..dcf438c5 100644
--- a/js/api/anthropic.js
+++ b/js/api/anthropic.js
@@ -25,6 +25,7 @@ export class Anthropic {
version = '';
model = '';
system_prompt = '';
+ temperature = '';
max_tokens = 4096;
stream = false;
@@ -33,6 +34,7 @@ export class Anthropic {
version = '',
model = '',
system_prompt = '',
+ temperature = '',
max_tokens = 4096,
stream = false,
} = {}) {
@@ -40,6 +42,7 @@ export class Anthropic {
this.version = version;
this.model = model;
this.system_prompt = system_prompt;
+ this.temperature = temperature;
this.max_tokens = max_tokens > 0 ? max_tokens : 4096;
this.stream = stream;
}
@@ -87,6 +90,19 @@ export class Anthropic {
// console.log(">>>>>>>>>>> Anthropic API request: " + JSON.stringify(messages));
try {
+
+ let claude_body = {
+ model: this.model,
+ max_tokens: parseInt(this.max_tokens),
+ system: this.system_prompt,
+ messages: messages,
+ stream: this.stream,
+ };
+
+ const tempFloat = parseFloat(this.temperature);
+
+ if(this.temperature != '' && !Number.isNaN(tempFloat)) claude_body.temperature = tempFloat;
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
@@ -95,13 +111,7 @@ export class Anthropic {
"anthropic-version": this.version,
"anthropic-dangerous-direct-browser-access": "true",
},
- body: JSON.stringify({
- model: this.model,
- max_tokens: this.max_tokens,
- system: this.system_prompt,
- messages: messages,
- stream: this.stream,
- }),
+ body: JSON.stringify(claude_body),
});
return response;
}catch (error) {
diff --git a/js/api/google_gemini.js b/js/api/google_gemini.js
index f2e2eb67..e16c52e5 100644
--- a/js/api/google_gemini.js
+++ b/js/api/google_gemini.js
@@ -25,6 +25,7 @@ export class GoogleGemini {
system_instruction = '';
stream = false;
thinking_budget = ''; // Model default
+ temperature = ''; // no temperature defined
constructor({
apiKey = '',
@@ -32,12 +33,14 @@ export class GoogleGemini {
system_instruction = '',
stream = false,
thinking_budget = '',
+ temperature = '',
} = {}) {
this.apiKey = apiKey;
this.model = model;
this.system_instruction = system_instruction;
this.stream = stream;
this.thinking_budget = String(thinking_budget ?? '').trim();
+ this.temperature = String(temperature ?? '').trim();
/* Info from: https://ai.google.dev/gemini-api/docs/thinking?#set-budget
# Turn on thinking with a specific token limit: "thinking_budget": 1024
# Thinking off: "thinking_budget": 0
@@ -87,7 +90,8 @@ export class GoogleGemini {
try {
let google_gemini_body = {
- contents:messages
+ contents: messages,
+ generationConfig: {},
};
// console.log("[ThunderAI] Google Gemini API system_instruction: " + JSON.stringify(this.system_instruction));
@@ -101,13 +105,17 @@ export class GoogleGemini {
}
if(this.thinking_budget !== '') {
- google_gemini_body.generationConfig = {
- thinkingConfig: {
+ google_gemini_body.generationConfig.thinkingConfig = {
thinking_budget: this.thinking_budget,
- }
};
}
+ const tempFloat = parseFloat(this.temperature);
+
+ if(this.temperature != '' && !Number.isNaN(tempFloat)) {
+ google_gemini_body.generationConfig.temperature = tempFloat;
+ }
+
// console.log("[ThunderAI] Google Gemini API request: " + JSON.stringify(google_gemini_body));
const response = await fetch("https://generativelanguage.googleapis.com/v1beta/models/" + this.model + ":" + (this.stream ? 'streamGenerateContent?alt=sse&' : 'generateContent?') + "key=" + this.apiKey, {
diff --git a/js/api/ollama.js b/js/api/ollama.js
index 9a4be836..f9f7e0ad 100644
--- a/js/api/ollama.js
+++ b/js/api/ollama.js
@@ -22,6 +22,7 @@ export class Ollama {
model = '';
stream = false;
num_ctx = 0;
+ temperature = '';
think = false;
constructor({
@@ -29,12 +30,14 @@ export class Ollama {
model = '',
stream = false,
num_ctx = 0,
+ temperature = '',
think = false,
} = {}) {
this.host = (host || '').trim().replace(/\/+$/, "");
this.model = model;
this.stream = stream;
this.num_ctx = num_ctx;
+ this.temperature = temperature;
this.think = think;
}
@@ -78,6 +81,7 @@ export class Ollama {
fetchResponse = async (messages) => {
try {
+ const tempFloat = parseFloat(this.temperature);
//console.log(">>>>>>>>>> messages: " +JSON.stringify(messages));
const response = await fetch(this.host + "/api/chat", {
method: "POST",
@@ -90,6 +94,7 @@ export class Ollama {
stream: this.stream,
think: this.think,
...(this.num_ctx > 0 ? { options: { num_ctx: parseInt(this.num_ctx) } } : {}),
+ ...(this.temperature != '' && !Number.isNaN(tempFloat) ? { options: { temperature: tempFloat } } : {}),
}),
});
return response;
@@ -103,30 +108,4 @@ export class Ollama {
}
}
-
- // fetchResponse = async (messages) => {
- // try {
- // let sending = messages.join(' ');
- // console.log(">>>>>>>>>> sending: " + sending);
- // const response = await fetch(this.host + "/api/generate", {
- // method: "POST",
- // headers: {
- // "Content-Type": "application/json",
- // },
- // body: JSON.stringify({
- // model: this.model,
- // prompt: sending,
- // stream: this.stream,
- // }),
- // });
- // return response;
- // }catch (error) {
- // console.error("[ThunderAI] Ollama API request failed: " + error);
- // let output = {};
- // output.is_exception = true;
- // output.ok = false;
- // output.error = "Ollama API request failed: " + error;
- // return output;
- // }
- // }
}
diff --git a/js/api/openai_comp.js b/js/api/openai_comp.js
index 3e975b62..683a0b8c 100644
--- a/js/api/openai_comp.js
+++ b/js/api/openai_comp.js
@@ -26,6 +26,7 @@ export class OpenAIComp {
apiKey = '';
use_v1 = true;
stream = false;
+ temperature = '';
constructor({
host = '',
@@ -33,12 +34,14 @@ export class OpenAIComp {
apiKey = '',
stream = false,
use_v1 = true,
+ temperature = '',
} = {}) {
this.host = (host || '').trim().replace(/\/+$/, "");
this.model = model;
this.stream = stream;
this.apiKey = apiKey;
this.use_v1 = use_v1;
+ this.temperature = temperature;
}
@@ -78,6 +81,7 @@ export class OpenAIComp {
fetchResponse = async (messages, maxTokens = 0) => {
try{
+ const tempFloat = parseFloat(this.temperature);
const curr_headers = {
"Content-Type": "application/json",
};
@@ -91,7 +95,8 @@ export class OpenAIComp {
model: this.model,
messages: messages,
stream: this.stream,
- ...(maxTokens > 0 ? { 'max_tokens': parseInt(maxTokens) } : {})
+ ...(maxTokens > 0 ? { 'max_tokens': parseInt(maxTokens) } : {}),
+ ...(this.temperature != '' && !Number.isNaN(tempFloat) ? { 'temperature': tempFloat } : {})
}),
});
return response;
diff --git a/js/api/openai.js b/js/api/openai_responses.js
similarity index 71%
rename from js/api/openai.js
rename to js/api/openai_responses.js
index 6d589c4c..174c4f4d 100644
--- a/js/api/openai.js
+++ b/js/api/openai_responses.js
@@ -24,6 +24,7 @@ export class OpenAI {
apiKey = '';
model = '';
developer_messages = '';
+ temperature = '';
stream = false;
store = false;
@@ -31,12 +32,14 @@ export class OpenAI {
apiKey = '',
model = '',
developer_messages = '',
+ temperature = '',
stream = false,
store = false
} = {}) {
this.apiKey = apiKey;
this.model = model;
this.developer_messages = developer_messages;
+ this.temperature = temperature;
this.stream = stream;
this.store = store;
}
@@ -65,7 +68,7 @@ export class OpenAI {
let output = {};
output.ok = true;
let output_response = await response.json();
- output.response = output_response.data.filter(item => item.id.startsWith('gpt-')).sort((a, b) => b.id.localeCompare(a.id));
+ output.response = output_response.data.filter(item => item.id.startsWith('gpt-') || item.id.startsWith('o1-') || item.id.startsWith('o4-') || item.id.startsWith('o3-')).sort((a, b) => b.id.localeCompare(a.id));
return output;
}catch (error) {
@@ -78,52 +81,49 @@ export class OpenAI {
}
}
- fetchResponse = async (messages, maxTokens = 0) => {
+ fetchResponse = async (messages, maxTokens = 0, previous_response_id = null) => {
+
+ const input = messages.map(msg => ({
+ role: msg.role,
+ content: [{ type: "input_text", text: msg.content }]
+ }));
+
+ const tempFloat = parseFloat(this.temperature);
+
+ let request_body = {
+ model: this.model,
+ input: input,
+ stream: this.stream,
+ store: this.store,
+ ...(this.temperature != '' && !Number.isNaN(tempFloat) ? { 'temperature': tempFloat } : {}),
+ ...(maxTokens > 0 ? { 'max_output_tokens': parseInt(maxTokens) } : {}),
+ ...(previous_response_id && this.store ? { 'previous_response_id': previous_response_id } : {})
+ }
if(this.developer_messages !== ''){
- messages.push({role: "developer", content: [{"type": "text", "text": this.developer_messages}]});
+ request_body.instructions = this.developer_messages;
}
// console.log(">>>>>>>>>>> OpenAI API request: " + JSON.stringify(messages));
try {
- const response = await fetch("https://api.openai.com/v1/chat/completions", {
+ const response = await fetch("https://api.openai.com/v1/responses", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer "+ this.apiKey
},
- body: JSON.stringify({
- model: this.model,
- messages: messages,
- stream: this.stream,
- store: this.store,
- ...(maxTokens > 0 ? { 'max_tokens': parseInt(maxTokens) } : {})
- }),
+ body: JSON.stringify(request_body),
});
return response;
}catch (error) {
- console.error("[ThunderAI] OpenAI API request failed: " + error);
+ console.error("[ThunderAI] OpenAI Responses API request failed: " + error);
let output = {};
output.is_exception = true;
output.ok = false;
- output.error = "OpenAI API request failed: " + error;
+ output.error = "OpenAI Responses API request failed: " + error;
return output;
}
}
- async countTokensUsingAPI(model, text) {
- const response = await fetch('https://api.openai.com/v1/engines/'+model+'/tokenizer', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': 'Bearer ' + this.apiKey
- },
- body: JSON.stringify({ text })
- });
-
- const data = await response.json();
- return data.token_count;
- }
-
-}
+}
\ No newline at end of file
diff --git a/js/mzta-menus.js b/js/mzta-menus.js
index 241daebe..c2a54999 100644
--- a/js/mzta-menus.js
+++ b/js/mzta-menus.js
@@ -19,7 +19,10 @@
// Some original methods are derived from https://github.com/ali-raheem/Aify/blob/cfadf52f576b7be3720b5b73af7c8d3129c054da/plugin/html/actions.js
import { getPrompts } from './mzta-prompts.js';
-import { prefs_default } from '../options/mzta-options-default.js';
+import {
+ prefs_default,
+ getDynamicSettingsDefaults
+} from '../options/mzta-options-default.js';
import {
getLanguageDisplayName,
getMenuContextCompose,
@@ -222,7 +225,8 @@ export class mzta_Menus {
prompt: fullPrompt,
llm: def_conntype,
custom_model: curr_prompt.model ? curr_prompt.model : '',
- do_debug: prefs_at.do_debug
+ do_debug: prefs_at.do_debug,
+ config: curr_prompt
});
await cmd_addTags.initWorker();
try{
@@ -258,9 +262,11 @@ export class mzta_Menus {
connection_type: prefs_default.connection_type,
calendar_enforce_timezone: prefs_default.calendar_enforce_timezone,
calendar_timezone: prefs_default.calendar_timezone,
+ ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type'])
});
- if((prefs_at.connection_type === '')||(prefs_at.connection_type === null)||(prefs_at.connection_type === undefined)||(prefs_at.connection_type === 'chatgpt_web')){
- console.error("[ThunderAI | GetCalendarEvent] Invalid connection type: " + prefs_at.connection_type);
+ let def_conntype = getConnectionType(prefs_at, curr_prompt, 'get_calendar_event');
+ if((def_conntype === '')||(def_conntype === null)||(def_conntype === undefined)||(def_conntype === 'chatgpt_web')){
+ console.error("[ThunderAI | GetCalendarEvent] Invalid connection type: " + def_conntype);
taWorkingStatus.stopWorking();
return {ok:'0'};
}
@@ -277,8 +283,9 @@ export class mzta_Menus {
this.logger.log("fullPrompt: " + fullPrompt);
let cmd_GetCalendarEvent = new mzta_specialCommand({
prompt: fullPrompt,
- llm: prefs_at.connection_type,
- do_debug: true
+ llm: def_conntype,
+ do_debug: true,
+ config: curr_prompt
});
await cmd_GetCalendarEvent.initWorker();
try{
@@ -333,9 +340,14 @@ export class mzta_Menus {
}
case 'prompt_get_task': { // Get a task info
let task_data = '';
- let prefs_at = await browser.storage.sync.get({connection_type: '', calendar_enforce_timezone: false, calendar_timezone: '',});
- if((prefs_at.connection_type === '')||(prefs_at.connection_type === null)||(prefs_at.connection_type === undefined)||(prefs_at.connection_type === 'chatgpt_web')){
- console.error("[ThunderAI | GetTask] Invalid connection type: " + prefs_at.connection_type);
+ let prefs_at = await browser.storage.sync.get({
+ connection_type: '',
+ calendar_enforce_timezone: false,
+ calendar_timezone: '',
+ ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type'])});
+ let def_conntype = getConnectionType(prefs_at, curr_prompt, 'get_task');
+ if((def_conntype === '')||(def_conntype === null)||(def_conntype === undefined)||(def_conntype === 'chatgpt_web')){
+ console.error("[ThunderAI | GetTask] Invalid connection type: " + def_conntype);
taWorkingStatus.stopWorking();
return {ok:'0'};
}
@@ -349,8 +361,9 @@ export class mzta_Menus {
this.logger.log("fullPrompt: " + fullPrompt);
let cmd_GetTask = new mzta_specialCommand({
prompt: fullPrompt,
- llm: prefs_at.connection_type,
- do_debug: true
+ llm: def_conntype,
+ do_debug: true,
+ config: curr_prompt
});
await cmd_GetTask.initWorker();
try{
diff --git a/js/mzta-placeholders-autocomplete.js b/js/mzta-placeholders-autocomplete.js
index 3bf1463b..d4f03f3d 100644
--- a/js/mzta-placeholders-autocomplete.js
+++ b/js/mzta-placeholders-autocomplete.js
@@ -34,7 +34,7 @@ export function textareaAutocomplete(textarea, suggestions, type_value = -1) {
type = tr.querySelector('.type_output').value
}
// console.log(">>>>>>>>> type: " + type);
- // console.log(">>>>>>>>> suggestions: " + JSON.stringify(suggestions));
+ // console.log(">>>>>>>>> suggestions: " + JSON.stringify(suggestions));
// console.log(">>>>>>>>> lastWord: " + lastWord);
const matches = suggestions.filter(s => s.command.startsWith(lastWord) && (String(s.type) == String(type) || String(s.type) == '0' )).map(s => s.command);
// console.log(">>>>>>>>> matches: " + JSON.stringify(matches));
@@ -106,6 +106,7 @@ export function textareaAutocomplete(textarea, suggestions, type_value = -1) {
}
function insertAutocomplete(suggestion, textarea) {
+ // console.log(">>>>>>>>> insertAutocomplete suggestion: " + JSON.stringify(suggestion));
const cursorPosition = textarea.selectionStart;
const textBefore = textarea.value.substring(0, cursorPosition);
const textAfter = textarea.value.substring(cursorPosition);
@@ -115,7 +116,7 @@ export function textareaAutocomplete(textarea, suggestions, type_value = -1) {
const completion = suggestion.substring(lastWord.length);
const newText = textBefore + completion + textAfter;
textarea.value = newText;
- const newCursorPosition = cursorPosition + completion.length;
+ const newCursorPosition = cursorPosition + completion.length - (suggestion.endsWith(':%}') ? 2 : 0);
textarea.setSelectionRange(newCursorPosition, newCursorPosition);
}
}
diff --git a/js/mzta-placeholders.js b/js/mzta-placeholders.js
index 0eb6f926..27eeb858 100644
--- a/js/mzta-placeholders.js
+++ b/js/mzta-placeholders.js
@@ -17,6 +17,7 @@
*/
import { prefs_default } from '../options/mzta-options-default.js';
+import { getMailHeader } from './mzta-utils.js';
/* ================= PLACEHOLDERS PROPERTIES ========================================
@@ -35,6 +36,10 @@ import { prefs_default } from '../options/mzta-options-default.js';
0: Custom placeholder
1: Default placeholder (not editable, cannot be deleted)
+ is_dynamic attribute:
+ 0: it's a fixed placeholder
+ 1: it's a dynamic placehoder (it means that it will have a : and then a value, like {%my_placeholder:test_value%})
+
================ USER PROPERTIES
enabled attribute:
0: Disabled
@@ -53,6 +58,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -61,6 +67,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -69,6 +76,7 @@ const defaultPlaceholders = [
default_value: "",
type: 2,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -77,6 +85,7 @@ const defaultPlaceholders = [
default_value: "",
type: 2,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -85,6 +94,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -93,6 +103,7 @@ const defaultPlaceholders = [
default_value: "",
type: 1,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -101,6 +112,16 @@ const defaultPlaceholders = [
default_value: "",
type: 1,
is_default: "1",
+ is_dynamic: "0",
+ enabled: 1,
+ },
+ {
+ id: 'mail_headers',
+ name: "__MSG_placeholder_mail_headers__",
+ default_value: "",
+ type: 1,
+ is_default: "1",
+ is_dynamic: "1",
enabled: 1,
},
{
@@ -109,6 +130,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -117,6 +139,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -125,6 +148,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -133,6 +157,7 @@ const defaultPlaceholders = [
default_value: "0",
type: 1,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -141,6 +166,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -149,6 +175,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -157,6 +184,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -165,6 +193,7 @@ const defaultPlaceholders = [
default_value: "",
type: 1,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -173,6 +202,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -181,6 +211,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -189,6 +220,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -197,6 +229,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -205,6 +238,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -213,6 +247,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -221,6 +256,7 @@ const defaultPlaceholders = [
default_value: "",
type: 0,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
},
{
@@ -229,6 +265,7 @@ const defaultPlaceholders = [
default_value: "",
type: 1,
is_default: "1",
+ is_dynamic: "0",
enabled: 1,
}
];
@@ -269,6 +306,7 @@ export async function setCustomPlaceholders(placeholders) {
placeholders.forEach(ph => {
ph.id = placeholdersUtils.validateCustomDataPH_ID(ph.id);
ph.is_default = "0";
+ ph.is_dynamic = "0";
});
await browser.storage.local.set({_custom_placeholder: placeholders});
}
@@ -301,6 +339,14 @@ export async function prepareCustomDataPHsForImport(placeholders){
return output;
}
+export function mapPlaceholderToSuggestion(p) {
+ // console.log(">>>>>>>>>> mapPlaceholderToSuggestion p" + JSON.stringify(p));
+ return {
+ command: '{%' + p.id + (p.is_dynamic == 1 ? ':' : '') + '%}',
+ type: p.type,
+ is_dynamic: p.is_dynamic,
+ };
+}
export const placeholdersUtils = {
@@ -330,10 +376,21 @@ export const placeholdersUtils = {
// Use exec to find all matches
while ((match = regex.exec(text)) !== null) {
- const foundPH = activePHs.find(ph => ph.id === match[1].trim());
- if (foundPH) {
- matches.push(foundPH);
- }
+ // console.log(">>>>>>>>>> extractPlaceholders match: " + JSON.stringify(match));
+ const foundPH = activePHs.find(ph => ph.id === match[1].trim() || (ph.is_dynamic == 1 && match[1].startsWith(ph.id + ':')));
+ if (foundPH) {
+ if (foundPH.is_dynamic == 1 && match[1].includes(':')) {
+ const [id, custom_value] = match[1].split(':', 2);
+ const dynamicPH = { ...foundPH }; // Create a copy to avoid modifying the original
+ dynamicPH.id = id.trim();
+ dynamicPH.custom_value = custom_value.trim();
+ matches.push(dynamicPH);
+ // console.log(">>>>>>>>>> extractPlaceholders dynamicPH: " + JSON.stringify(dynamicPH));
+ } else {
+ matches.push(foundPH);
+ // console.log(">>>>>>>>>> extractPlaceholders foundPH: " + JSON.stringify(foundPH));
+ }
+ }
}
return matches;
@@ -346,13 +403,17 @@ export const placeholdersUtils = {
use_default_value = false,
skip_additional_text = false
} = args || {};
+ // console.log(">>>>>>>>>> replacePlaceholders replacements: " + JSON.stringify(replacements));
// Regular expression to match patterns like {%...%}
return text.replace(/{%\s*(.*?)\s*%}/g, function(match, p1) {
+ // console.log(">>>>>>>>>> replacePlaceholders match: " + JSON.stringify(match));
+ // console.log(">>>>>>>>>> replacePlaceholders p1: " + JSON.stringify(p1));
// p1 contains the key inside {% %}
if (skip_additional_text && (p1 === 'additional_text')) {
return match;
}
- const currPlaceholder = defaultPlaceholders.find(ph => ph.id === p1);
+ const currPlaceholder = defaultPlaceholders.find(ph => (ph.id === p1) || (ph.is_dynamic == 1 && p1.startsWith(ph.id + ':')));
+ // console.log(">>>>>>>>>> replacePlaceholders currPlaceholder: " + JSON.stringify(currPlaceholder));
if (!currPlaceholder) {
return match;
}
@@ -424,6 +485,7 @@ export const placeholdersUtils = {
// console.log(">>>>>>>>>> curr_message: " + JSON.stringify(curr_message));
let finalSubs = {};
for(let currPH of currPHs){
+ // console.log(">>>>>>>>>> currPH: " + JSON.stringify(currPH));
switch(currPH.id){
case 'mail_text_body':
finalSubs['mail_text_body'] = placeholdersUtils.failSafePlaceholders(body_text);
@@ -446,6 +508,9 @@ export const placeholdersUtils = {
case 'mail_folder_path':
finalSubs['mail_folder_path'] = placeholdersUtils.failSafePlaceholders(curr_message.folder?.path);
break;
+ case 'mail_headers':
+ finalSubs['mail_headers:' + currPH.custom_value] = placeholdersUtils.failSafePlaceholders(await getMailHeader(curr_message, currPH.custom_value));
+ break;
case 'selected_text':
finalSubs['selected_text'] = placeholdersUtils.failSafePlaceholders(selection_text);
break;
@@ -506,7 +571,7 @@ export const placeholdersUtils = {
break;
}
}
-
+ // console.log(">>>>>>>>>> finalSubs: " + JSON.stringify(finalSubs));
return finalSubs;
},
@@ -516,6 +581,6 @@ export const placeholdersUtils = {
return '';
}
return element;
- }
+ },
-}
+}
\ No newline at end of file
diff --git a/js/mzta-special-commands.js b/js/mzta-special-commands.js
index 0bbab812..95a0d14f 100644
--- a/js/mzta-special-commands.js
+++ b/js/mzta-special-commands.js
@@ -17,7 +17,10 @@
*/
// Call the API to use a special prompt
- import { prefs_default } from "../options/mzta-options-default.js";
+ import {
+ prefs_default,
+ integration_options_config
+ } from "../options/mzta-options-default.js";
import { taLogger } from './mzta-logger.js';
export class mzta_specialCommand {
@@ -29,131 +32,84 @@
full_message = "";
logger = null;
do_debug = false;
+ config = {};
constructor(args = {}) {
let {
prompt = '',
llm = '',
custom_model = '',
- do_debug = false
+ do_debug = false,
+ config = {}
} = args;
this.prompt = prompt;
this.llm = llm;
this.custom_model = custom_model;
+ this.config = config;
this.logger = new taLogger('mzta_specialCommand', do_debug);
this.do_debug = do_debug;
- switch (this.llm) {
- case "chatgpt_api":
- this.worker = new Worker(new URL('./workers/model-worker-openai.js', import.meta.url), { type: 'module' });
- break;
- case "google_gemini_api":
- this.worker = new Worker(new URL('./workers/model-worker-google_gemini.js', import.meta.url), { type: 'module' });
- break;
- case "ollama_api":
- this.worker = new Worker(new URL('./workers/model-worker-ollama.js', import.meta.url), { type: 'module' });
- break;
- case "openai_comp_api":
- this.worker = new Worker(new URL('./workers/model-worker-openai_comp.js', import.meta.url), { type: 'module' });
- break;
- case "anthropic_api":
- this.worker = new Worker(new URL('./workers/model-worker-anthropic.js', import.meta.url), { type: 'module' });
- break;
- default:
- this.logger.log("Invalid LLM type: " + this.llm);
- throw new Error("Invalid LLM type: " + this.llm);
+
+ const worker_path_map = {
+ chatgpt_api: './workers/model-worker-openai_responses.js',
+ google_gemini_api: './workers/model-worker-google_gemini.js',
+ ollama_api: './workers/model-worker-ollama.js',
+ openai_comp_api: './workers/model-worker-openai_comp.js',
+ anthropic_api: './workers/model-worker-anthropic.js',
+ };
+
+ const worker_path = worker_path_map[this.llm];
+ if (worker_path) {
+ this.worker = new Worker(new URL(worker_path, import.meta.url), { type: 'module' });
+ } else {
+ this.logger.log("Invalid LLM type: " + this.llm);
+ throw new Error("Invalid LLM type: " + this.llm);
}
}
async initWorker() {
- // console.log((">>>>>>>>>>>> this.custom_model: " + this.custom_model));
- switch (this.llm) {
- case "chatgpt_api": {
- let prefs_api = await browser.storage.sync.get({
- chatgpt_api_key: prefs_default.chatgpt_api_key,
- chatgpt_model: prefs_default.chatgpt_model,
- chatgpt_developer_messages: prefs_default.chatgpt_developer_messages,
- });
- this.worker.postMessage({
- type: 'init',
- chatgpt_api_key: prefs_api.chatgpt_api_key,
- chatgpt_model: this.custom_model != '' ? this.custom_model : prefs_api.chatgpt_model,
- chatgpt_developer_messages: prefs_api.chatgpt_developer_messages,
- do_debug: this.do_debug,
- i18nStrings: ''
- });
- break;
+ const integration = this.llm.replace('_api', '');
+ const options_config = integration_options_config[integration];
+
+ if (!options_config) {
+ this.logger.error("Invalid integration type: " + integration);
+ throw new Error("Invalid integration type: " + integration);
+ }
+
+ let prefsToGet = {};
+ for (const key in options_config) {
+ const prefKey = `${integration}_${key}`;
+ prefsToGet[prefKey] = prefs_default[prefKey];
+ }
+
+ let prefs_api = await browser.storage.sync.get(prefsToGet);
+
+ let workerInitMessage = {
+ type: 'init',
+ do_debug: this.do_debug,
+ i18nStrings: ''
+ };
+
+ for (const key in options_config) {
+ const prefKey = `${integration}_${key}`;
+
+ const configValue = this.config[prefKey];
+
+ if (configValue !== undefined) {
+ if (typeof options_config[key] === 'boolean') {
+ prefs_api[prefKey] = (configValue === true || configValue === 'true' || configValue === 1);
+ } else {
+ prefs_api[prefKey] = configValue;
+ }
}
- case "google_gemini_api": {
- let prefs_api = await browser.storage.sync.get({
- google_gemini_api_key: prefs_default.google_gemini_api_key,
- google_gemini_model: prefs_default.google_gemini_model,
- google_gemini_system_instruction: prefs_default.google_gemini_system_instruction,
- google_gemini_thinking_budget: prefs_default.google_gemini_thinking_budget,
- });
- this.worker.postMessage({
- type: 'init',
- google_gemini_api_key: prefs_api.google_gemini_api_key,
- google_gemini_model: this.custom_model != '' ? this.custom_model : prefs_api.google_gemini_model,
- google_gemini_system_instruction: prefs_api.google_gemini_system_instruction,
- google_gemini_thinking_budget: prefs_api.google_gemini_thinking_budget,
- do_debug: this.do_debug,
- i18nStrings: ''
- });
- break;
- }
- case "ollama_api": {
- let prefs_api = await browser.storage.sync.get({
- ollama_host: prefs_default.ollama_host,
- ollama_model: prefs_default.ollama_model,
- });
- this.worker.postMessage({
- type: 'init',
- ollama_host: prefs_api.ollama_host,
- ollama_model: this.custom_model != '' ? this.custom_model : prefs_api.ollama_model,
- do_debug: this.do_debug,
- i18nStrings: ''
- });
- break;
- }
- case "openai_comp_api": {
- let prefs_api = await browser.storage.sync.get({
- openai_comp_host: prefs_default.openai_comp_host,
- openai_comp_model: prefs_default.openai_comp_model,
- openai_comp_api_key: prefs_default.openai_comp_api_key,
- openai_comp_use_v1: prefs_default.openai_comp_use_v1,
- openai_comp_chat_name: prefs_default.openai_comp_chat_name,
- do_debug: prefs_default.do_debug,
- });
- this.worker.postMessage({
- type: 'init',
- openai_comp_host: prefs_api.openai_comp_host,
- openai_comp_model: this.custom_model != '' ? this.custom_model : prefs_api.openai_comp_model,
- openai_comp_api_key: prefs_api.openai_comp_api_key,
- openai_comp_use_v1: prefs_api.openai_comp_use_v1,
- do_debug: this.do_debug,
- i18nStrings: ''
- });
- break;
- }
- case "anthropic_api": {
- let prefs_api = await browser.storage.sync.get({
- anthropic_api_key: prefs_default.anthropic_api_key,
- anthropic_model: prefs_default.anthropic_model,
- anthropic_version: prefs_default.anthropic_version,
- anthropic_max_tokens: prefs_default.anthropic_max_tokens,
- });
- this.worker.postMessage({
- type: 'init',
- anthropic_api_key: prefs_api.anthropic_api_key,
- anthropic_model: this.custom_model != '' ? this.custom_model : prefs_api.anthropic_model,
- anthropic_version: prefs_api.anthropic_version,
- anthropic_max_tokens: prefs_api.anthropic_max_tokens,
- do_debug: this.do_debug,
- i18nStrings: ''
- });
- break;
+
+ if (key === 'model') {
+ workerInitMessage[prefKey] = this.custom_model !== '' ? this.custom_model : prefs_api[prefKey];
+ } else {
+ workerInitMessage[prefKey] = prefs_api[prefKey];
}
}
+
+ this.worker.postMessage(workerInitMessage);
}
sendPrompt(){
diff --git a/js/mzta-utils.js b/js/mzta-utils.js
index 24cb954c..c833bb31 100644
--- a/js/mzta-utils.js
+++ b/js/mzta-utils.js
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-import { prefs_default } from '../options/mzta-options-default.js';
+import { prefs_default, getDynamicSettingValue } from '../options/mzta-options-default.js';
const sparks_min = '1.2.0'; // Minimum version of ThunderAI-Sparks required for the add-on to work
export const ChatGPTWeb_models = ['gpt-5','gpt-5-instant','gpt-5-t-mini','gpt-5-thinking']; // List of models available in ChatGPT Web
@@ -189,6 +189,17 @@ export async function replaceBody(tabId, replyHtml) {
await messenger.compose.setComposeDetails(tabId, {body: fullBody});
}
+export async function getMailHeader(curr_message, mail_header_id) {
+ let mail_header_value = "";
+ let full_message = await browser.messages.getFull(curr_message.id);
+ // console.log(">>>>>>>>>>>> getMailHeader full_message: " + JSON.stringify(full_message));
+ if(full_message.hasOwnProperty("headers") && Object.keys(full_message.headers).some(header => header.toLowerCase() === mail_header_id.toLowerCase())){
+ mail_header_value = full_message.headers[Object.keys(full_message.headers).find(header => header.toLowerCase() === mail_header_id.toLowerCase())];
+ }
+ // console.log(">>>>>>>>>>>> getMailHeader mail_header_value: " + mail_header_value)
+ return mail_header_value;
+}
+
export function sanitizeHtml(input) {
// Keep only tags and remove all other HTML tags
return input.replace(/<(?!br\s*\/?)[^>]+>/gi, '');
@@ -612,19 +623,33 @@ export function extractJsonObject(inputString) {
}
export function isAPIKeyValue(id){
- return id=="chatgpt_api_key" || id=="openai_comp_api_key" || id=="google_gemini_api_key" || id=="anthropic_api_key";
+ return id.endsWith('_api_key');
}
-export function getConnectionType(conntype, prompt, use_promptspecific_api = true) {
- if(!use_promptspecific_api) {
- return conntype;
- }
- // console.log(">>>>>>>>>>> getConnectionType conntype: " + conntype + " prompt: " + JSON.stringify(prompt));
- if (prompt?.api != null && prompt.api !== '') {
- return prompt.api;
- } else {
- return conntype;
- }
+export function getConnectionType(prefsOrType, prompt, prefixOrSpecific = null) {
+ let defaultType = '';
+ let specificType = '';
+
+ if (typeof prefsOrType === 'object' && prefsOrType !== null) {
+ // New signature: (prefs, prompt, prefix)
+ defaultType = prefsOrType.connection_type;
+ if (typeof prefixOrSpecific === 'string' && prefixOrSpecific) {
+ const prefix = prefixOrSpecific;
+ const useSpecific = getDynamicSettingValue(prefsOrType, prefix, 'use_specific_integration');
+ if (useSpecific) {
+ specificType = getDynamicSettingValue(prefsOrType, prefix, 'connection_type');
+ }
+ }
+ } else {
+ // Old signature / Direct usage: (connection_type_string, prompt, [specific_type_string])
+ if (typeof prefixOrSpecific === 'string') {
+ specificType = prefixOrSpecific;
+ }
+ }
+
+ if (specificType && specificType !== '') return specificType;
+ if (prompt && prompt.api && prompt.api !== '') return prompt.api;
+ return defaultType;
}
export async function checkSparksPresence() {
diff --git a/js/workers/model-worker-anthropic.js b/js/workers/model-worker-anthropic.js
index f750e5c8..9af3dc17 100644
--- a/js/workers/model-worker-anthropic.js
+++ b/js/workers/model-worker-anthropic.js
@@ -23,8 +23,6 @@
import { Anthropic } from '../api/anthropic.js';
import { taLogger } from '../mzta-logger.js';
-let anthropic_api_key = null;
-let anthropic_model = '';
let anthropic = null;
let stopStreaming = false;
let i18nStrings = null;
@@ -37,16 +35,15 @@ let assistantResponseAccumulator = '';
self.onmessage = async function(event) {
if (event.data.type === 'init') {
// console.log(">>>>>>>>>>>>>> event.data: " + JSON.stringify(event.data));
- anthropic_api_key = event.data.anthropic_api_key;
- anthropic_model = event.data.anthropic_model;
- anthropic = new Anthropic({
- apiKey: anthropic_api_key,
- version: event.data.anthropic_version,
- model: anthropic_model,
- system_prompt: event.data.anthropic_system_prompt,
- max_tokens: event.data.anthropic_max_tokens,
- stream: true
- });
+ let config = { stream: true };
+ for (const key in event.data) {
+ if (key.startsWith('anthropic_')) {
+ let newKey = key.replace('anthropic_', '');
+ if (newKey === 'api_key') newKey = 'apiKey';
+ config[newKey] = event.data[key];
+ }
+ }
+ anthropic = new Anthropic(config);
do_debug = event.data.do_debug;
i18nStrings = event.data.i18nStrings;
taLog = new taLogger('model-worker-anthropic', do_debug);
diff --git a/js/workers/model-worker-google_gemini.js b/js/workers/model-worker-google_gemini.js
index cce3cf8b..4b6fc138 100644
--- a/js/workers/model-worker-google_gemini.js
+++ b/js/workers/model-worker-google_gemini.js
@@ -23,8 +23,6 @@
import { GoogleGemini } from '../api/google_gemini.js';
import { taLogger } from '../mzta-logger.js';
-let google_gemini_api_key = null;
-let google_gemini_model = '';
let google_gemini = null;
let stopStreaming = false;
let i18nStrings = null;
@@ -36,15 +34,15 @@ let assistantResponseAccumulator = '';
self.onmessage = async function(event) {
if (event.data.type === 'init') {
- google_gemini_api_key = event.data.google_gemini_api_key;
- google_gemini_model = event.data.google_gemini_model;
- google_gemini = new GoogleGemini({
- apiKey: google_gemini_api_key,
- model: google_gemini_model,
- system_instruction: event.data.google_gemini_system_instruction,
- thinking_budget: event.data.google_gemini_thinking_budget,
- stream: true
- });
+ let config = { stream: true };
+ for (const key in event.data) {
+ if (key.startsWith('google_gemini_')) {
+ let newKey = key.replace('google_gemini_', '');
+ if (newKey === 'api_key') newKey = 'apiKey';
+ config[newKey] = event.data[key];
+ }
+ }
+ google_gemini = new GoogleGemini(config);
do_debug = event.data.do_debug;
i18nStrings = event.data.i18nStrings;
taLog = new taLogger('model-worker-google_gemini', do_debug);
diff --git a/js/workers/model-worker-ollama.js b/js/workers/model-worker-ollama.js
index d099cf2a..3987df33 100644
--- a/js/workers/model-worker-ollama.js
+++ b/js/workers/model-worker-ollama.js
@@ -23,9 +23,6 @@
import { Ollama } from '../api/ollama.js';
import { taLogger } from '../mzta-logger.js';
-let ollama_host = null;
-let ollama_model = '';
-let ollama_num_ctx = 0;
let ollama = null;
let stopStreaming = false;
let i18nStrings = null;
@@ -38,16 +35,14 @@ let assistantResponseAccumulator = '';
self.onmessage = async function(event) {
switch (event.data.type) {
case 'init':
- ollama_host = event.data.ollama_host;
- ollama_model = event.data.ollama_model;
- ollama_num_ctx = event.data.ollama_num_ctx;
- //console.log(">>>>>>>>>>> ollama_host: " + ollama_host);
- ollama = new Ollama({
- host: ollama_host,
- model: ollama_model,
- stream: true,
- num_ctx: ollama_num_ctx
- });
+ let config = { stream: true };
+ for (const key in event.data) {
+ if (key.startsWith('ollama_')) {
+ let newKey = key.replace('ollama_', '');
+ config[newKey] = event.data[key];
+ }
+ }
+ ollama = new Ollama(config);
do_debug = event.data.do_debug;
i18nStrings = event.data.i18nStrings;
taLog = new taLogger('model-worker-ollama', do_debug);
diff --git a/js/workers/model-worker-openai_comp.js b/js/workers/model-worker-openai_comp.js
index 333ce148..a541ffa8 100644
--- a/js/workers/model-worker-openai_comp.js
+++ b/js/workers/model-worker-openai_comp.js
@@ -23,10 +23,6 @@
import { OpenAIComp } from '../api/openai_comp.js';
import { taLogger } from '../mzta-logger.js';
-let openai_comp_host = null;
-let openai_comp_model = '';
-let openai_comp_api_key = '';
-let openai_comp_use_v1 = true;
let openai_comp = null;
let stopStreaming = false;
let i18nStrings = null;
@@ -38,17 +34,15 @@ let assistantResponseAccumulator = '';
self.onmessage = async function(event) {
if (event.data.type === 'init') {
- openai_comp_host = event.data.openai_comp_host;
- openai_comp_model = event.data.openai_comp_model;
- openai_comp_api_key = event.data.openai_comp_api_key;
- openai_comp_use_v1 = event.data.openai_comp_use_v1;
- openai_comp = new OpenAIComp({
- host: openai_comp_host,
- model: openai_comp_model,
- apiKey: openai_comp_api_key,
- stream: true,
- use_v1: openai_comp_use_v1
- });
+ let config = { stream: true };
+ for (const key in event.data) {
+ if (key.startsWith('openai_comp_')) {
+ let newKey = key.replace('openai_comp_', '');
+ if (newKey === 'api_key') newKey = 'apiKey';
+ config[newKey] = event.data[key];
+ }
+ }
+ openai_comp = new OpenAIComp(config);
do_debug = event.data.do_debug;
i18nStrings = event.data.i18nStrings;
taLog = new taLogger('model-worker-openai_comp', do_debug);
diff --git a/js/workers/model-worker-openai.js b/js/workers/model-worker-openai_responses.js
similarity index 73%
rename from js/workers/model-worker-openai.js
rename to js/workers/model-worker-openai_responses.js
index 1cd9055b..89803673 100644
--- a/js/workers/model-worker-openai.js
+++ b/js/workers/model-worker-openai_responses.js
@@ -20,11 +20,9 @@
* The original code has been released under the Apache License, Version 2.0.
*/
-import { OpenAI } from '../api/openai.js';
+import { OpenAI } from '../api/openai_responses.js';
import { taLogger } from '../mzta-logger.js';
-let chatgpt_api_key = null;
-let chatgpt_model = '';
let openai = null;
let stopStreaming = false;
let i18nStrings = null;
@@ -33,25 +31,36 @@ let taLog = null;
let conversationHistory = [];
let assistantResponseAccumulator = '';
+let previous_response_id = null;
self.onmessage = async function(event) {
if (event.data.type === 'init') {
- chatgpt_api_key = event.data.chatgpt_api_key;
- chatgpt_model = event.data.chatgpt_model;
- openai = new OpenAI({
- apiKey: chatgpt_api_key,
- model: chatgpt_model,
- developer_messages: event.data.chatgpt_developer_messages,
- stream: true,
- store: event.data.chatgpt_api_store
- });
+ let config = { stream: true };
+ for (const key in event.data) {
+ if (key.startsWith('chatgpt_')) {
+ if (key.startsWith('chatgpt_web_')) continue; // Exclude chatgpt_web_ prefixed keys
+ let newKey = key.replace('chatgpt_', '');
+ if (newKey === 'api_key') newKey = 'apiKey';
+ config[newKey] = event.data[key];
+ }
+ }
+ openai = new OpenAI(config);
do_debug = event.data.do_debug;
i18nStrings = event.data.i18nStrings;
- taLog = new taLogger('model-worker-openai', do_debug);
+ taLog = new taLogger('model-worker-openai_responses', do_debug);
+ previous_response_id = null;
} else if (event.data.type === 'chatMessage') {
conversationHistory.push({ role: 'user', content: event.data.message });
- const response = await openai.fetchResponse(conversationHistory); //4096);
+ let messagesToSend = conversationHistory;
+ if (previous_response_id) {
+ messagesToSend = [conversationHistory[conversationHistory.length - 1]];
+ taLog.log("previous_response_id: " + previous_response_id);
+ } else {
+ taLog.log("no previous_response_id");
+ }
+
+ const response = await openai.fetchResponse(messagesToSend, 0, previous_response_id);
postMessage({ type: 'messageSent' });
if (!response.ok) {
@@ -102,9 +111,10 @@ self.onmessage = async function(event) {
let parsedLines = [];
try{
parsedLines = lines
+ .map((line) => line.trim())
+ .filter((line) => line.startsWith("data:"))
.map((line) => line.replace(/^data: /, "").trim()) // Remove the "data: " prefix
.filter((line) => line !== "" && line !== "[DONE]") // Remove empty lines and "[DONE]"
- // .map((line) => JSON.parse(line)); // Parse the JSON string
.map((line) => {
try {
taLog.log("line: " + JSON.stringify(line));
@@ -120,13 +130,13 @@ self.onmessage = async function(event) {
}
for (const parsedLine of parsedLines) {
- const { choices } = parsedLine;
- const { delta } = choices[0];
- const { content } = delta;
- // Update the UI with the new content
- if (content) {
- assistantResponseAccumulator += content;
- postMessage({ type: 'newToken', payload: { token: content } });
+ if (parsedLine.type === 'response.created' && parsedLine.response && parsedLine.response.id){
+ previous_response_id = parsedLine.response.id;
+ } else if (parsedLine.type === 'response.output_text.delta' && parsedLine.delta) {
+ assistantResponseAccumulator += parsedLine.delta;
+ postMessage({ type: 'newToken', payload: { token: parsedLine.delta } });
+ // } else if (parsedLine.type === 'response.completed' && parsedLine.response && parsedLine.response.id) {
+ // previous_response_id = parsedLine.response.id;
}
}
}
diff --git a/manifest.json b/manifest.json
index a3cef247..e18ffa08 100644
--- a/manifest.json
+++ b/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "ThunderAI",
"description": "__MSG_extensionDescription__",
- "version": "3.7.8",
+ "version": "3.8.0pre3",
"author": "Mic (m@micz.it)",
"homepage_url": "https://micz.it/thunderbird-addon-thunderai/",
"browser_specific_settings": {
diff --git a/mzta-background.js b/mzta-background.js
index c0199a7d..daab589e 100644
--- a/mzta-background.js
+++ b/mzta-background.js
@@ -17,7 +17,11 @@
*/
import { mzta_script } from './js/mzta-chatgpt.js';
-import { prefs_default } from './options/mzta-options-default.js';
+import {
+ prefs_default,
+ getDynamicSettingValue,
+ getDynamicSettingsDefaults
+} from './options/mzta-options-default.js';
import { mzta_Menus } from './js/mzta-menus.js';
import { taLogger } from './js/mzta-logger.js';
import {
@@ -838,10 +842,7 @@ async function reload_pref_init(){
dynamic_menu_force_enter: prefs_default.dynamic_menu_force_enter,
add_tags_context_menu: prefs_default.add_tags_context_menu,
spamfilter_context_menu: prefs_default.spamfilter_context_menu,
- add_tags_use_specific_integration: prefs_default.add_tags_use_specific_integration,
- add_tags_connection_type: prefs_default.add_tags_connection_type,
- spamfilter_use_specific_integration: prefs_default.spamfilter_use_specific_integration,
- spamfilter_connection_type: prefs_default.spamfilter_connection_type
+ ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type'])
});
_process_incoming = prefs_init.add_tags_auto || prefs_init.spamfilter;
_sparks_presence = await checkSparksPresence();
@@ -1073,8 +1074,7 @@ async function processEmails(messages, addTagsAuto, spamFilter) {
add_tags_auto_uselist: prefs_default.add_tags_auto_uselist,
add_tags_auto_uselist_list: prefs_default.add_tags_auto_uselist_list,
spamfilter_enabled_accounts: prefs_default.spamfilter_enabled_accounts,
- add_tags_use_specific_integration: prefs_default.add_tags_use_specific_integration,
- spamfilter_use_specific_integration: prefs_default.spamfilter_use_specific_integration,
+ ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type']),
do_debug: prefs_default.do_debug,
});
@@ -1121,9 +1121,10 @@ async function processEmails(messages, addTagsAuto, spamFilter) {
// console.log(">>>>>>>>>> curr_prompt_add_tags.model: " + curr_prompt_add_tags.model);
let cmd_addTags = new mzta_specialCommand({
prompt: specialFullPrompt_add_tags,
- llm: getConnectionType(prefs_aats.connection_type, curr_prompt_add_tags, prefs_aats.add_tags_use_specific_integration),
+ llm: getConnectionType(prefs_aats, curr_prompt_add_tags, 'add_tags'),
custom_model: curr_prompt_add_tags.model ? curr_prompt_add_tags.model : '',
- do_debug: prefs_aats.do_debug
+ do_debug: prefs_aats.do_debug,
+ config: curr_prompt_add_tags
});
await cmd_addTags.initWorker();
let tags_current_email = [];
@@ -1160,9 +1161,10 @@ async function processEmails(messages, addTagsAuto, spamFilter) {
// console.log(">>>>>>>> Special prompt for spamfilter: " + specialFullPrompt_spamfilter);
let cmd_spamfilter = new mzta_specialCommand({
prompt: specialFullPrompt_spamfilter,
- llm: getConnectionType(prefs_aats.connection_type, curr_prompt_spamfilter, prefs_aats.spamfilter_use_specific_integration),
+ llm: getConnectionType(prefs_aats, curr_prompt_spamfilter, 'spamfilter'),
custom_model: curr_prompt_spamfilter.model ? curr_prompt_spamfilter.model : '',
- do_debug: prefs_aats.do_debug
+ do_debug: prefs_aats.do_debug,
+ config: curr_prompt_spamfilter
});
await cmd_spamfilter.initWorker();
let spamfilter_result = '';
diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js
index 48174267..7eaa771f 100644
--- a/options/mzta-options-default.js
+++ b/options/mzta-options-default.js
@@ -16,7 +16,86 @@
* along with this program. If not, see .
*/
+const special_prompts_with_integration = ['add_tags', 'spamfilter'];
+
+export const integration_options_config = {
+ chatgpt: {
+ api_key: '',
+ model: '',
+ developer_messages: '',
+ temperature: '',
+ store: false
+ },
+ ollama: {
+ host: '',
+ model: '',
+ num_ctx: 0,
+ temperature: '',
+ think: false
+ },
+ openai_comp: {
+ host: '',
+ model: '',
+ api_key: '',
+ use_v1: true,
+ chat_name: 'OpenAI Comp',
+ temperature: ''
+ },
+ google_gemini: {
+ api_key: '',
+ model: '',
+ system_instruction: '',
+ thinking_budget: '',
+ temperature: ''
+ },
+ anthropic: {
+ api_key: '',
+ model: '',
+ version: '2023-06-01',
+ max_tokens: 4096,
+ system_prompt: '',
+ temperature: ''
+ }
+};
+
+const integration_settings_template = {
+ use_specific_integration: false,
+ connection_type: 'chatgpt_api',
+};
+
+const global_integration_settings = { ...integration_settings_template };
+
+for (const [integration, options] of Object.entries(integration_options_config)) {
+ for (const [key, value] of Object.entries(options)) {
+ global_integration_settings[`${integration}_${key}`] = value;
+ }
+}
+
+let generated_prefs = {};
+
+special_prompts_with_integration.forEach(prompt_prefix => {
+ for (const [key, value] of Object.entries(integration_settings_template)) {
+ generated_prefs[`${prompt_prefix}_${key}`] = value;
+ }
+});
+
+export function getDynamicSettingsDefaults(keysFilter = []) {
+ let defaults = {};
+ special_prompts_with_integration.forEach(prefix => {
+ const keys = keysFilter.length > 0 ? keysFilter : Object.keys(integration_settings_template);
+ keys.forEach(key => {
+ defaults[`${prefix}_${key}`] = prefs_default[`${prefix}_${key}`];
+ });
+ });
+ return defaults;
+}
+
+export function getDynamicSettingValue(prefs, prefix, settingName) {
+ return prefs[`${prefix}_${settingName}`];
+}
+
export const prefs_default = {
+ ...global_integration_settings,
do_debug: false,
chatgpt_win_height: 800,
chatgpt_win_width: 700,
@@ -29,28 +108,6 @@ export const prefs_default = {
chatgpt_web_tempchat: false,
chatgpt_web_project: '',
chatgpt_web_custom_gpt: '',
- chatgpt_api_key: '',
- chatgpt_api_store: false,
- chatgpt_model: '',
- chatgpt_developer_messages: '',
- ollama_host: '',
- ollama_model: '',
- ollama_num_ctx: 0,
- ollama_think: false,
- openai_comp_host: '', // For OpenAI Compatible API as LM-Studio
- openai_comp_model: '',
- openai_comp_api_key: '',
- openai_comp_use_v1: true,
- openai_comp_chat_name: 'OpenAI Comp',
- google_gemini_api_key: '',
- google_gemini_model: '',
- google_gemini_system_instruction: '',
- google_gemini_thinking_budget: '',
- anthropic_api_key: '',
- anthropic_model: '',
- anthropic_version: '2023-06-01',
- anthropic_max_tokens: 4096,
- anthropic_system_prompt: '',
dynamic_menu_force_enter: false,
dynamic_menu_order_alphabet: true,
placeholders_use_default_value: false,
@@ -68,13 +125,6 @@ export const prefs_default = {
add_tags_auto_uselist_list: '',
add_tags_context_menu: true,
add_tags_enabled_accounts: [],
- add_tags_use_specific_integration: false,
- add_tags_connection_type: 'chatgpt_api',
- add_tags_chatgpt_model: '',
- add_tags_ollama_model: '',
- add_tags_openai_comp_model: '',
- add_tags_google_gemini_model: '',
- add_tags_anthropic_model: '',
get_calendar_event: true,
get_task: true,
calendar_enforce_timezone: false,
@@ -83,12 +133,6 @@ export const prefs_default = {
spamfilter_threshold: 70,
spamfilter_context_menu: true,
spamfilter_enabled_accounts: [],
- spamfilter_use_specific_integration: false,
- spamfilter_connection_type: 'chatgpt_api',
- spamfilter_chatgpt_model: '',
- spamfilter_ollama_model: '',
- spamfilter_openai_comp_model: '',
- spamfilter_google_gemini_model: '',
- spamfilter_anthropic_model: '',
auto_summary_enabled: false, // Enable automatic AI summarization for message previews
+ ...generated_prefs
}
diff --git a/options/mzta-options.js b/options/mzta-options.js
index 92d2989a..2f8c4424 100644
--- a/options/mzta-options.js
+++ b/options/mzta-options.js
@@ -16,7 +16,7 @@
* along with this program. If not, see .
*/
-import { prefs_default } from './mzta-options-default.js';
+import { prefs_default, getDynamicSettingsDefaults } from './mzta-options-default.js';
import { taLogger } from '../js/mzta-logger.js';
import {
ChatGPTWeb_models,
@@ -127,7 +127,11 @@ function disable_MaxPromptLength(){
function disable_AddTags(prefs_opt){
let add_tags = document.getElementById('add_tags');
let conntype_select = document.getElementById("connection_type");
- let add_tags_disabled = (getConnectionType(conntype_select.value, {api: prefs_opt.add_tags_use_specific_integration ? prefs_opt.add_tags_connection_type : ''}) === "chatgpt_web");
+ const tempPrefs = {
+ connection_type: conntype_select.value,
+ ...prefs_opt
+ };
+ let add_tags_disabled = (getConnectionType(tempPrefs, null, 'add_tags') === "chatgpt_web");
// console.log('>>>>>>>>>>>>> add_tags_disabled: ' + add_tags_disabled);
add_tags.checked = add_tags_disabled ? false : add_tags.checked;
let add_tags_checked_original = add_tags.checked;
@@ -145,7 +149,11 @@ function disable_AddTags(prefs_opt){
function disable_SpamFilter(prefs_opt){
let spamfilter = document.getElementById('spamfilter');
let conntype_select = document.getElementById("connection_type");
- let spamfilter_disabled = (getConnectionType(conntype_select.value, {api: prefs_opt.spamfilter_use_specific_integration ? prefs_opt.spamfilter_connection_type : ''}) === "chatgpt_web");;
+ const tempPrefs = {
+ connection_type: conntype_select.value,
+ ...prefs_opt
+ };
+ let spamfilter_disabled = (getConnectionType(tempPrefs, null, 'spamfilter') === "chatgpt_web");
let spamfilter_checked_original = spamfilter.checked;
spamfilter.checked = spamfilter_disabled ? false : spamfilter.checked;
if(!spamfilter.checked){
@@ -302,10 +310,7 @@ document.addEventListener('DOMContentLoaded', async () => {
});
let prefs_opt = await browser.storage.sync.get({
- add_tags_use_specific_integration: prefs_default.add_tags_use_specific_integration,
- add_tags_connection_type: prefs_default.add_tags_connection_type,
- spamfilter_use_specific_integration: prefs_default.spamfilter_use_specific_integration,
- spamfilter_connection_type: prefs_default.spamfilter_connection_type,
+ ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type'])
});
let conntype_select = document.getElementById("connection_type");
diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html
index d021cc39..6107811c 100644
--- a/options/mzta-release-notes.html
+++ b/options/mzta-release-notes.html
@@ -7,6 +7,18 @@
ThunderAI Release Notes
+
Version 3.8.0 - ??/??/2025
+
+ [All APIs] When using special prompts (like automatically adding tags or the spam filter) with a specific API integration, all the settings for that integration can be specific. In this way you can use different api keys for the same integration, or different system prompt or temperature [#590 ].
+ [All APIs] Added the temperature parameter [#561 ].
+ [OpenAI API] Model filtering improved when choosing a model in the options page.
+ [OpenAI API] Now using the new Responses API [#407 ].
+ It is now possible to define a custom placeholder with dynamic data to retrieve any header present in the current email [#527 ].
+ [All APIs] The configuration information reported in the webchat API has been improved for all integrations.
+ Spanish (es) translation added, thanks to Gerardo Sobarzo , Andrés Rendón Hernández , Erick Limon .
+ Various fixes.
+ ...
+
Version 3.7.8 - 18/12/2025
Greek (el) translation added, thanks to ChristosK. .
diff --git a/pages/_lib/connection-ui.css b/pages/_lib/connection-ui.css
index 5b63569d..1f4a3263 100644
--- a/pages/_lib/connection-ui.css
+++ b/pages/_lib/connection-ui.css
@@ -1,25 +1,25 @@
tr.conntype_chatgpt_api, tr.conntype_chatgpt_api2{
- background-color: rgb(185, 220, 252);
+ background-color: rgb(185, 220, 252) !important;
}
tr.conntype_chatgpt_web, tr.conntype_chatgpt_web2{
- background-color: rgb(255, 209, 183);
+ background-color: rgb(255, 209, 183) !important;
}
tr.conntype_ollama_api, tr.conntype_ollama_api2{
- background-color: rgb(177, 238, 169);
+ background-color: rgb(177, 238, 169) !important;
}
tr.conntype_openai_comp_api, tr.conntype_openai_comp_api2{
- background-color: rgb(213, 169, 238);
+ background-color: rgb(213, 169, 238) !important;
}
tr.conntype_google_gemini_api, tr.conntype_google_gemini_api2{
- background-color: rgb(233, 238, 169);
+ background-color: rgb(233, 238, 169) !important;
}
tr.conntype_anthropic_api, tr.conntype_anthropic_api2{
- background-color: rgb(255, 168, 204);
+ background-color: rgb(255, 168, 204) !important;
}
#chatgpt_model_fetch_loading{
@@ -37,6 +37,10 @@ textarea.option-textarea{
height: 10em;
}
+label{
+ width: -moz-available;
+}
+
.api_key-container {
position: relative;
width: 100%;
@@ -89,27 +93,27 @@ span.opt_title{
@media (prefers-color-scheme: dark) {
tr.conntype_chatgpt_api, tr.conntype_chatgpt_api2{
- background-color: rgb(0, 0, 58);
+ background-color: rgb(0, 0, 58) !important;
}
tr.conntype_chatgpt_web,tr.conntype_chatgpt_web2{
- background-color: rgb(39, 11, 0);
+ background-color: rgb(39, 11, 0) !important;
}
tr.conntype_ollama_api, tr.conntype_ollama_api2{
- background-color: rgb(7, 58, 0);
+ background-color: rgb(7, 58, 0) !important;
}
tr.conntype_openai_comp_api, tr.conntype_openai_comp_api2{
- background-color: rgb(89, 20, 129);
+ background-color: rgb(89, 20, 129) !important;
}
tr.conntype_google_gemini_api, tr.conntype_google_gemini_api2{
- background-color: rgb(72, 77, 3);
+ background-color: rgb(72, 77, 3) !important;
}
tr.conntype_anthropic_api, tr.conntype_anthropic_api2{
- background-color: rgb(83, 0, 35);
+ background-color: rgb(83, 0, 35) !important;
}
.api_key-container .toggle-icon img {
diff --git a/pages/_lib/connection-ui.js b/pages/_lib/connection-ui.js
index ef285b3a..550a1491 100644
--- a/pages/_lib/connection-ui.js
+++ b/pages/_lib/connection-ui.js
@@ -16,8 +16,11 @@
* along with this program. If not, see .
*/
-import { prefs_default } from '../../options/mzta-options-default.js';
-import { OpenAI } from '../../js/api/openai.js';
+import {
+ prefs_default,
+ integration_options_config
+} from '../../options/mzta-options-default.js';
+import { OpenAI } from '../../js/api/openai_responses.js';
import { Ollama } from '../../js/api/ollama.js';
import { OpenAIComp } from '../../js/api/openai_comp.js'
import { GoogleGemini } from '../../js/api/google_gemini.js';
@@ -28,6 +31,7 @@ import {
sanitizeChatGPTWebCustomData
} from '../../js/mzta-utils.js';
import { openAICompConfigs } from '../../js/api/openai_comp_configs.js';
+import { loadPrompt, savePrompt, clearPromptAPI } from '../../js/mzta-prompts.js';
export const varConnectionUI = {
permission_all_urls: false
@@ -40,7 +44,9 @@ export async function injectConnectionUI({
no_chatgpt_web = false,
defaultType = '',
tr_class = '',
- taLog = console
+ taLog = console,
+ customButtonLabel = '',
+ customButtonCallback = null
} = {}) {
const anchorTr = document.getElementById(afterTrId);
@@ -49,6 +55,19 @@ export async function injectConnectionUI({
return null;
}
+ // Inject CSS if not present
+ if (!document.getElementById('mzta-connection-ui-style')) {
+ const style = document.createElement('style');
+ style.id = 'mzta-connection-ui-style';
+ style.textContent = `
+ .api_key-container { position: relative; display: flex; align-items: center; }
+ .toggle-icon { cursor: pointer; margin-left: 5px; }
+ .toggle-icon img { width: 16px; height: 16px; vertical-align: middle; }
+ .option-input { flex-grow: 1; }
+ `;
+ document.head.appendChild(style);
+ }
+
let tpl = `
@@ -57,8 +76,9 @@ export async function injectConnectionUI({
-
+
+ ${customButtonLabel ? `${customButtonLabel} ` : ''}
@@ -137,9 +157,9 @@ export async function injectConnectionUI({
@@ -150,9 +170,22 @@ export async function injectConnectionUI({
- __MSG_ChatGPT_Models_Fetch__ __MSG_Loading__
+ __MSG_ChatGPT_Models_Fetch__ __MSG_Loading__
-
+
+
+
+
+
+
+
+ __MSG_prefs_api_temperature__
+
+
+
+
+
+ __MSG_prefs_chatgpt_api_temperature_Info__
@@ -164,7 +197,7 @@ export async function injectConnectionUI({
-
+
__MSG_ChatGPT_chatgpt_api_store_info__
@@ -177,7 +210,7 @@ export async function injectConnectionUI({
-
+
__MSG_ChatGPT_Developer_Messages_Info__
@@ -189,9 +222,9 @@ export async function injectConnectionUI({
@@ -202,9 +235,22 @@ export async function injectConnectionUI({
- __MSG_GoogleGemini_Models_Fetch__ __MSG_Loading__
+ __MSG_GoogleGemini_Models_Fetch__ __MSG_Loading__
-
+
+
+
+
+
+
+
+ __MSG_prefs_api_temperature__
+
+
+
+
+
+ __MSG_prefs_google_gemini_temperature_Info__
@@ -216,7 +262,7 @@ export async function injectConnectionUI({
-
+
__MSG_prefs_google_gemini_thinking_budget_Info__
__MSG_more_info_string__
@@ -230,7 +276,7 @@ export async function injectConnectionUI({
-
+
__MSG_GoogleGemini_SystemInstruction_Info__
@@ -242,16 +288,16 @@ export async function injectConnectionUI({
-
+
-
+
__MSG_remember_CORS__ [__MSG_more_info_string__ ]
__MSG_CORS_alternative_1__
__MSG_CORS_alternative_2__
- __MSG_CORS_give_allurls_perm__
+ __MSG_CORS_give_allurls_perm__
@@ -261,9 +307,22 @@ export async function injectConnectionUI({
- __MSG_Ollama_Models_Fetch__ __MSG_Loading__
+ __MSG_Ollama_Models_Fetch__ __MSG_Loading__
-
+
+
+
+
+
+
+
+ __MSG_prefs_api_temperature__
+
+
+
+
+
+ __MSG_prefs_ollama_temperature_Info__
@@ -273,7 +332,7 @@ export async function injectConnectionUI({
-
+
__MSG_prefs_ollama_think_Info__
@@ -284,7 +343,7 @@ export async function injectConnectionUI({
-
+
__MSG_prefs_ollama_num_ctx_Info__
@@ -295,7 +354,7 @@ export async function injectConnectionUI({
-
+
__MSG_prefs_OpenAIComp_AvailableServices_Info__
@@ -307,17 +366,17 @@ export async function injectConnectionUI({
-
+
__MSG_prefs_OptionText_openai_comp_info_remote__
-
+
__MSG_maybe_CORS_openai_comp__ [__MSG_more_info_string__ ]
__MSG_CORS_alternative_1__
__MSG_CORS_alternative_2__
- __MSG_CORS_give_allurls_perm__
+ __MSG_CORS_give_allurls_perm__
@@ -326,7 +385,7 @@ export async function injectConnectionUI({
-
+
__MSG_prefs_OptionText_openai_comp_use_v1_info__
@@ -339,9 +398,9 @@ export async function injectConnectionUI({
@@ -349,14 +408,14 @@ export async function injectConnectionUI({
__MSG_OpenAIComp_Models__
- __MSG_prefs_OpenAIComp_ForceModel__
- __MSG_prefs_OpenAIComp_ClearModelsList__
+ __MSG_prefs_OpenAIComp_ForceModel__
+ __MSG_prefs_OpenAIComp_ClearModelsList__
- __MSG_OpenAIComp_Models_Fetch__ __MSG_Loading__
+ __MSG_OpenAIComp_Models_Fetch__ __MSG_Loading__
-
+
@@ -366,11 +425,24 @@ export async function injectConnectionUI({
-
+
__MSG_prefs_OpenAIComp_ChatName_Info__
+
+
+
+ __MSG_prefs_api_temperature__
+
+
+
+
+
+ __MSG_prefs_openai_comp_temperature_Info__
+
+
+
__MSG_prefs_Anthropic_API_Key__
@@ -378,9 +450,9 @@ export async function injectConnectionUI({
@@ -391,9 +463,22 @@ export async function injectConnectionUI({
- __MSG_Anthropic_Models_Fetch__ __MSG_Loading__
+ __MSG_Anthropic_Models_Fetch__ __MSG_Loading__
-
+
+
+
+
+
+
+
+ __MSG_prefs_api_temperature__
+
+
+
+
+
+ __MSG_prefs_anthropic_temperature_Info__
@@ -405,7 +490,7 @@ export async function injectConnectionUI({
-
+
__MSG_Anthropic_System_Prompt_Info__
@@ -418,7 +503,7 @@ export async function injectConnectionUI({
-
+
__MSG_Anthropic_Version_Info__ https://docs.anthropic.com/en/api/versioning
@@ -427,7 +512,7 @@ export async function injectConnectionUI({
__MSG_prefs_OptionText_anthropic_max_tokens__
-
+
__MSG_prefs_OptionText_anthropic_max_tokens_Info__
@@ -448,6 +533,8 @@ export async function injectConnectionUI({
}
});
+ const getPrefixedId = (id) => `${modelId_prefix ? `${modelId_prefix}` : ''}${id}`;
+
// Bindings
// const bindClick = (id, cb) => { const el = document.getElementById(id); if (el && typeof cb === 'function') el.addEventListener('click', cb); };
// const bindChange = (id, cb) => { const el = document.getElementById(id); if (el && typeof cb === 'function') el.addEventListener('change', cb); };
@@ -470,18 +557,18 @@ export async function injectConnectionUI({
conntype_select.addEventListener("change", (ev) => warn_Anthropic_VersionEmpty(modelId_prefix));
document.getElementById("chatgpt_web_project").addEventListener("input", validateCustomData_ChatGPTWeb);
document.getElementById("chatgpt_web_custom_gpt").addEventListener("input", validateCustomData_ChatGPTWeb);
- document.getElementById("chatgpt_api_key").addEventListener("change", (ev) => warn_ChatGPT_APIKeyEmpty(modelId_prefix));
- document.getElementById("ollama_host").addEventListener("change", (ev) => warn_Ollama_HostEmpty(modelId_prefix));
- document.getElementById("openai_comp_host").addEventListener("change", (ev) => warn_OpenAIComp_HostEmpty(modelId_prefix));
- document.getElementById("google_gemini_api_key").addEventListener("change", (ev) => warn_GoogleGemini_APIKeyEmpty(modelId_prefix));
- document.getElementById("anthropic_api_key").addEventListener("change", (ev) => warn_Anthropic_APIKeyEmpty(modelId_prefix));
- document.getElementById("anthropic_version").addEventListener("change", (ev) => warn_Anthropic_VersionEmpty(modelId_prefix));
- document.getElementById("openai_comp_host").addEventListener("input", resetOpenAICompConfigs);
- document.getElementById("openai_comp_chat_name").addEventListener("input", resetOpenAICompConfigs);
- document.getElementById("openai_comp_use_v1").addEventListener("input", resetOpenAICompConfigs);
+ document.getElementById(getPrefixedId("chatgpt_api_key")).addEventListener("change", (ev) => warn_ChatGPT_APIKeyEmpty(modelId_prefix));
+ document.getElementById(getPrefixedId("ollama_host")).addEventListener("change", (ev) => warn_Ollama_HostEmpty(modelId_prefix));
+ document.getElementById(getPrefixedId("openai_comp_host")).addEventListener("change", (ev) => warn_OpenAIComp_HostEmpty(modelId_prefix));
+ document.getElementById(getPrefixedId("google_gemini_api_key")).addEventListener("change", (ev) => warn_GoogleGemini_APIKeyEmpty(modelId_prefix));
+ document.getElementById(getPrefixedId("anthropic_api_key")).addEventListener("change", (ev) => warn_Anthropic_APIKeyEmpty(modelId_prefix));
+ document.getElementById(getPrefixedId("anthropic_version")).addEventListener("change", (ev) => warn_Anthropic_VersionEmpty(modelId_prefix));
+ document.getElementById(getPrefixedId("openai_comp_host")).addEventListener("input", () => resetOpenAICompConfigs(modelId_prefix));
+ document.getElementById(getPrefixedId("openai_comp_chat_name")).addEventListener("input", () => resetOpenAICompConfigs(modelId_prefix));
+ document.getElementById(getPrefixedId("openai_comp_use_v1")).addEventListener("input", () => resetOpenAICompConfigs(modelId_prefix));
showConnectionOptions(conntype_select);
- loadOpenAICompConfigs();
+ loadOpenAICompConfigs(modelId_prefix);
warn_ChatGPT_APIKeyEmpty(modelId_prefix);
warn_Ollama_HostEmpty(modelId_prefix);
warn_OpenAIComp_HostEmpty(modelId_prefix);
@@ -489,9 +576,9 @@ export async function injectConnectionUI({
warn_Anthropic_APIKeyEmpty(modelId_prefix);
warn_Anthropic_VersionEmpty(modelId_prefix);
- const passwordField_chatgpt_api_key = document.getElementById('chatgpt_api_key');
- const toggleIcon_chatgpt_api_key = document.getElementById('toggle_chatgpt_api_key');
- const icon_img_chatgpt_api_key = document.getElementById('pwd-icon_chatgpt_api_key');
+ const passwordField_chatgpt_api_key = document.getElementById(getPrefixedId('chatgpt_api_key'));
+ const toggleIcon_chatgpt_api_key = document.getElementById(getPrefixedId('toggle_chatgpt_api_key'));
+ const icon_img_chatgpt_api_key = document.getElementById(getPrefixedId('pwd-icon_chatgpt_api_key'));
toggleIcon_chatgpt_api_key.addEventListener('click', () => {
const type = passwordField_chatgpt_api_key.getAttribute('type') === 'password' ? 'text' : 'password';
@@ -500,9 +587,9 @@ export async function injectConnectionUI({
icon_img_chatgpt_api_key.src = type === 'password' ? "/images/pwd-show.png" : "/images/pwd-hide.png";
});
- const passwordField_google_gemini_api_key = document.getElementById('google_gemini_api_key');
- const toggleIcon_google_gemini_api_key = document.getElementById('toggle_google_gemini_api_key');
- const icon_img_google_gemini_api_key = document.getElementById('pwd-icon_google_gemini_api_key');
+ const passwordField_google_gemini_api_key = document.getElementById(getPrefixedId('google_gemini_api_key'));
+ const toggleIcon_google_gemini_api_key = document.getElementById(getPrefixedId('toggle_google_gemini_api_key'));
+ const icon_img_google_gemini_api_key = document.getElementById(getPrefixedId('pwd-icon_google_gemini_api_key'));
toggleIcon_google_gemini_api_key.addEventListener('click', () => {
const type = passwordField_google_gemini_api_key.getAttribute('type') === 'password' ? 'text' : 'password';
@@ -511,9 +598,9 @@ export async function injectConnectionUI({
icon_img_google_gemini_api_key.src = type === 'password' ? "/images/pwd-show.png" : "/images/pwd-hide.png";
});
- const passwordField_openai_comp_api_key = document.getElementById('openai_comp_api_key');
- const toggleIcon_openai_comp_api_key = document.getElementById('toggle_openai_comp_api_key');
- const icon_img_openai_comp_api_key = document.getElementById('pwd-icon_openai_comp_api_key');
+ const passwordField_openai_comp_api_key = document.getElementById(getPrefixedId('openai_comp_api_key'));
+ const toggleIcon_openai_comp_api_key = document.getElementById(getPrefixedId('toggle_openai_comp_api_key'));
+ const icon_img_openai_comp_api_key = document.getElementById(getPrefixedId('pwd-icon_openai_comp_api_key'));
toggleIcon_openai_comp_api_key.addEventListener('click', () => {
const type = passwordField_openai_comp_api_key.getAttribute('type') === 'password' ? 'text' : 'password';
@@ -522,9 +609,9 @@ export async function injectConnectionUI({
icon_img_openai_comp_api_key.src = type === 'password' ? "/images/pwd-show.png" : "/images/pwd-hide.png";
});
- const passwordField_anthropic_api_key = document.getElementById('anthropic_api_key');
- const toggleIcon_anthropic_api_key = document.getElementById('toggle_anthropic_api_key');
- const icon_img_anthropic_api_key = document.getElementById('pwd-icon_anthropic_api_key');
+ const passwordField_anthropic_api_key = document.getElementById(getPrefixedId('anthropic_api_key'));
+ const toggleIcon_anthropic_api_key = document.getElementById(getPrefixedId('toggle_anthropic_api_key'));
+ const icon_img_anthropic_api_key = document.getElementById(getPrefixedId('pwd-icon_anthropic_api_key'));
toggleIcon_anthropic_api_key.addEventListener('click', () => {
const type = passwordField_anthropic_api_key.getAttribute('type') === 'password' ? 'text' : 'password';
@@ -558,7 +645,7 @@ export async function injectConnectionUI({
browser.tabs.create({ url: base_url + model_opt });
});
- let select_openai_comp_services_shortcut = document.getElementById('openai_comp_services_shortcut');
+ let select_openai_comp_services_shortcut = document.getElementById(getPrefixedId('openai_comp_services_shortcut'));
select_openai_comp_services_shortcut.addEventListener("change", () => {
let selectedOption = select_openai_comp_services_shortcut.options[select_openai_comp_services_shortcut.selectedIndex];
const config = openAICompConfigs.find(cfg => cfg.id === selectedOption.value);
@@ -566,20 +653,20 @@ export async function injectConnectionUI({
if (!confirm(browser.i18n.getMessage('OpenAIComp_Configs_ConfirmApply', config.name))) {
return;
}
- document.getElementById('openai_comp_host').value = config.host || '';
+ document.getElementById(getPrefixedId('openai_comp_host')).value = config.host || '';
// Clear all options from the select except the first (placeholder) one
const openaiCompModelSelect = getModelEl('openai_comp_model', modelId_prefix);
openaiCompModelSelect.value = '';
while (openaiCompModelSelect.options.length > 0) {
openaiCompModelSelect.remove(0);
}
- document.getElementById('openai_comp_use_v1').checked = !!config.use_v1;
- document.getElementById('openai_comp_chat_name').value = config.chat_name || '';
+ document.getElementById(getPrefixedId('openai_comp_use_v1')).checked = !!config.use_v1;
+ document.getElementById(getPrefixedId('openai_comp_chat_name')).value = config.chat_name || '';
// Trigger change events if needed
- document.getElementById('openai_comp_host').dispatchEvent(new Event('change', { bubbles: true }));
+ document.getElementById(getPrefixedId('openai_comp_host')).dispatchEvent(new Event('change', { bubbles: true }));
getModelEl('openai_comp_model', modelId_prefix).dispatchEvent(new Event('change', { bubbles: true }));
- document.getElementById('openai_comp_use_v1').dispatchEvent(new Event('change', { bubbles: true }));
- document.getElementById('openai_comp_chat_name').dispatchEvent(new Event('change', { bubbles: true }));
+ document.getElementById(getPrefixedId('openai_comp_use_v1')).dispatchEvent(new Event('change', { bubbles: true }));
+ document.getElementById(getPrefixedId('openai_comp_chat_name')).dispatchEvent(new Event('change', { bubbles: true }));
}
});
@@ -593,14 +680,14 @@ export async function injectConnectionUI({
select_chatgpt_model.appendChild(chatgpt_option);
select_chatgpt_model.addEventListener("change", () => warn_ChatGPT_APIKeyEmpty(modelId_prefix));
- document.getElementById('btnUpdateChatGPTModels').addEventListener('click', async () => {
- document.getElementById('chatgpt_model_fetch_loading').style.display = 'inline';
+ document.getElementById(getPrefixedId('btnUpdateChatGPTModels')).addEventListener('click', async () => {
+ document.getElementById(getPrefixedId('chatgpt_model_fetch_loading')).style.display = 'inline';
let openai = new OpenAI({
- apiKey: document.getElementById("chatgpt_api_key").value,
+ apiKey: document.getElementById(getPrefixedId("chatgpt_api_key")).value,
});
let granted = await messenger.permissions.request({ origins: ["https://*.openai.com/*"] });
if(!granted){
- document.getElementById('chatgpt_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('chatgpt_model_fetch_loading')).style.display = 'none';
taLog.log("OpenAI API permission denied");
alert(browser.i18n.getMessage("Optional_Permission_Denied_Model_Fetching"));
return;
@@ -614,7 +701,7 @@ export async function injectConnectionUI({
} catch (e) {
errorDetail = data.error;
}
- document.getElementById('chatgpt_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('chatgpt_model_fetch_loading')).style.display = 'none';
console.error("[ThunderAI] " + browser.i18n.getMessage("ChatGPT_Models_Error_fetching"));
alert(browser.i18n.getMessage("ChatGPT_Models_Error_fetching")+": " + errorDetail);
return;
@@ -628,7 +715,7 @@ export async function injectConnectionUI({
select_chatgpt_model.appendChild(option);
}
});
- document.getElementById('chatgpt_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('chatgpt_model_fetch_loading')).style.display = 'none';
});
warn_ChatGPT_APIKeyEmpty(modelId_prefix);
@@ -642,10 +729,10 @@ export async function injectConnectionUI({
select_google_gemini_model.appendChild(google_gemini_option);
select_google_gemini_model.addEventListener("change", () => warn_GoogleGemini_APIKeyEmpty(modelId_prefix));
- document.getElementById('btnUpdateGoogleGeminiModels').addEventListener('click', async () => {
- document.getElementById('google_gemini_model_fetch_loading').style.display = 'inline';
+ document.getElementById(getPrefixedId('btnUpdateGoogleGeminiModels')).addEventListener('click', async () => {
+ document.getElementById(getPrefixedId('google_gemini_model_fetch_loading')).style.display = 'inline';
let google_gemini = new GoogleGemini({
- apiKey: document.getElementById("google_gemini_api_key").value,
+ apiKey: document.getElementById(getPrefixedId("google_gemini_api_key")).value,
});
google_gemini.fetchModels().then((data) => {
if(!data.ok){
@@ -656,7 +743,7 @@ export async function injectConnectionUI({
} catch (e) {
errorDetail = data.error;
}
- document.getElementById('google_gemini_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('google_gemini_model_fetch_loading')).style.display = 'none';
console.error("[ThunderAI] " + browser.i18n.getMessage("GoogleGemini_Models_Error_fetching"));
alert(browser.i18n.getMessage("GoogleGemini_Models_Error_fetching")+": " + errorDetail);
return;
@@ -670,7 +757,7 @@ export async function injectConnectionUI({
select_google_gemini_model.appendChild(option);
}
});
- document.getElementById('google_gemini_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('google_gemini_model_fetch_loading')).style.display = 'none';
});
warn_GoogleGemini_APIKeyEmpty(modelId_prefix);
@@ -684,15 +771,15 @@ export async function injectConnectionUI({
select_ollama_model.appendChild(ollama_option);
select_ollama_model.addEventListener("change", () => warn_Ollama_HostEmpty(modelId_prefix));
- document.getElementById('btnUpdateOllamaModels').addEventListener('click', async () => {
- document.getElementById('ollama_model_fetch_loading').style.display = 'inline';
+ document.getElementById(getPrefixedId('btnUpdateOllamaModels')).addEventListener('click', async () => {
+ document.getElementById(getPrefixedId('ollama_model_fetch_loading')).style.display = 'inline';
let ollama = new Ollama({
- host: document.getElementById("ollama_host").value,
+ host: document.getElementById(getPrefixedId("ollama_host")).value,
});
try {
let data = await ollama.fetchModels();
if(!data){
- document.getElementById('ollama_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('ollama_model_fetch_loading')).style.display = 'none';
console.error("[ThunderAI] " + browser.i18n.getMessage("Ollama_Models_Error_fetching"));
alert(browser.i18n.getMessage("Ollama_Models_Error_fetching"));
return;
@@ -705,13 +792,13 @@ export async function injectConnectionUI({
} catch (e) {
errorDetail = data.error;
}
- document.getElementById('ollama_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('ollama_model_fetch_loading')).style.display = 'none';
console.error("[ThunderAI] " + browser.i18n.getMessage("Ollama_Models_Error_fetching"));
alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")+": " + errorDetail);
return;
}
if(data.response.models.length == 0){
- document.getElementById('ollama_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('ollama_model_fetch_loading')).style.display = 'none';
console.error("[ThunderAI] " + browser.i18n.getMessage("Ollama_Models_Error_fetching"));
alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")+": " + browser.i18n.getMessage("API_Models_Error_NoModels"));
return;
@@ -725,9 +812,9 @@ export async function injectConnectionUI({
select_ollama_model.appendChild(option);
}
});
- document.getElementById('ollama_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('ollama_model_fetch_loading')).style.display = 'none';
} catch (error) {
- document.getElementById('ollama_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('ollama_model_fetch_loading')).style.display = 'none';
taLog.error(browser.i18n.getMessage("Ollama_Models_Error_fetching"));
alert(browser.i18n.getMessage("Ollama_Models_Error_fetching")+": " + error.message);
}
@@ -743,12 +830,12 @@ export async function injectConnectionUI({
select_openai_comp_model.appendChild(openai_comp_option);
select_openai_comp_model.addEventListener("change", () => warn_OpenAIComp_HostEmpty(modelId_prefix));
- document.getElementById('btnUpdateOpenAICompModels').addEventListener('click', async () => {
- document.getElementById('openai_comp_model_fetch_loading').style.display = 'inline';
+ document.getElementById(getPrefixedId('btnUpdateOpenAICompModels')).addEventListener('click', async () => {
+ document.getElementById(getPrefixedId('openai_comp_model_fetch_loading')).style.display = 'inline';
let openai_comp = new OpenAIComp({
- host: document.getElementById("openai_comp_host").value,
- apiKey: document.getElementById("openai_comp_api_key").value,
- use_v1: document.getElementById("openai_comp_use_v1").checked,
+ host: document.getElementById(getPrefixedId("openai_comp_host")).value,
+ apiKey: document.getElementById(getPrefixedId("openai_comp_api_key")).value,
+ use_v1: document.getElementById(getPrefixedId("openai_comp_use_v1")).checked,
});
openai_comp.fetchModels().then((data) => {
if(!data.ok){
@@ -759,7 +846,7 @@ export async function injectConnectionUI({
} catch (e) {
errorDetail = data.error;
}
- document.getElementById('openai_comp_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('openai_comp_model_fetch_loading')).style.display = 'none';
console.error("[ThunderAI] " + browser.i18n.getMessage("OpenAIComp_Models_Error_fetching"));
alert(browser.i18n.getMessage("OpenAIComp_Models_Error_fetching")+": " + errorDetail);
return;
@@ -773,7 +860,7 @@ export async function injectConnectionUI({
select_openai_comp_model.appendChild(option);
}
});
- document.getElementById('openai_comp_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('openai_comp_model_fetch_loading')).style.display = 'none';
});
warn_OpenAIComp_HostEmpty(modelId_prefix);
@@ -788,15 +875,15 @@ export async function injectConnectionUI({
select_anthropic_model.addEventListener("change", () => warn_Anthropic_APIKeyEmpty(modelId_prefix));
select_anthropic_model.addEventListener("change", () => warn_Anthropic_VersionEmpty(modelId_prefix));
- document.getElementById('btnUpdateAnthropicModels').addEventListener('click', async () => {
- document.getElementById('anthropic_model_fetch_loading').style.display = 'inline';
+ document.getElementById(getPrefixedId('btnUpdateAnthropicModels')).addEventListener('click', async () => {
+ document.getElementById(getPrefixedId('anthropic_model_fetch_loading')).style.display = 'inline';
let anthropic = new Anthropic({
- apiKey: document.getElementById("anthropic_api_key").value,
- version: document.getElementById("anthropic_version").value,
+ apiKey: document.getElementById(getPrefixedId("anthropic_api_key")).value,
+ version: document.getElementById(getPrefixedId("anthropic_version")).value,
});
let granted = await messenger.permissions.request({ origins: ["https://*.anthropic.com/*"] });
if(!granted){
- document.getElementById('anthropic_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('anthropic_model_fetch_loading')).style.display = 'none';
taLog.warn("Claude API web permission denied");
alert(browser.i18n.getMessage("Optional_Permission_Denied_Model_Fetching"));
return;
@@ -810,7 +897,7 @@ export async function injectConnectionUI({
} catch (e) {
errorDetail = data.error;
}
- document.getElementById('anthropic_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('anthropic_model_fetch_loading')).style.display = 'none';
console.error("[ThunderAI] " + browser.i18n.getMessage("Anthropic_Models_Error_fetching"));
alert(browser.i18n.getMessage("Anthropic_Models_Error_fetching")+": " + errorDetail);
return;
@@ -827,13 +914,13 @@ export async function injectConnectionUI({
select_anthropic_model.appendChild(option);
}
});
- document.getElementById('anthropic_model_fetch_loading').style.display = 'none';
+ document.getElementById(getPrefixedId('anthropic_model_fetch_loading')).style.display = 'none';
});
warn_Anthropic_APIKeyEmpty(modelId_prefix);
});
- document.getElementById('btnOpenAICompForceModel').addEventListener('click', () => {
+ document.getElementById(getPrefixedId('btnOpenAICompForceModel')).addEventListener('click', () => {
let modelName = prompt(browser.i18n.getMessage('OpenAIComp_force_model_ask')).trim();
if ((modelName !== null) && (modelName !== undefined) && (modelName !== '')) {
let select_openai_comp_model = getModelEl('openai_comp_model', modelId_prefix);
@@ -846,7 +933,7 @@ export async function injectConnectionUI({
}
});
- document.getElementById('btnOpenAICompClearModelsList').addEventListener('click', () => {
+ document.getElementById(getPrefixedId('btnOpenAICompClearModelsList')).addEventListener('click', () => {
if (!confirm(browser.i18n.getMessage('OpenAIComp_ClearModelsList_Confirm'))) {
return;
}
@@ -858,13 +945,17 @@ export async function injectConnectionUI({
select_openai_comp_model.dispatchEvent(new Event('change', { bubbles: true }));
});
- document.getElementById('btnGiveAllUrlsPermission_ollama_api').addEventListener('click', async () => {
+ document.getElementById(getPrefixedId('btnGiveAllUrlsPermission_ollama_api')).addEventListener('click', async () => {
varConnectionUI.permission_all_urls = await messenger.permissions.request({ origins: [""] });
});
- document.getElementById('btnGiveAllUrlsPermission_openai_comp_api').addEventListener('click', async () => {
+ document.getElementById(getPrefixedId('btnGiveAllUrlsPermission_openai_comp_api')).addEventListener('click', async () => {
varConnectionUI.permission_all_urls = await messenger.permissions.request({ origins: [""] });
});
+
+ document.querySelectorAll('.check-number').forEach(input => {
+ input.addEventListener('input', warn_InvalidNumber);
+ });
warn_ChatGPT_APIKeyEmpty(modelId_prefix);
warn_Ollama_HostEmpty(modelId_prefix);
@@ -873,12 +964,127 @@ export async function injectConnectionUI({
warn_Anthropic_APIKeyEmpty(modelId_prefix);
warn_Anthropic_VersionEmpty(modelId_prefix);
+ if (customButtonLabel && customButtonCallback) {
+ const btn = document.getElementById(`${modelId_prefix}customButton`);
+ if (btn) {
+ btn.addEventListener('click', (e) => {
+ e.preventDefault();
+ customButtonCallback(e);
+ });
+ }
+ }
+
return {
select: conntype_select,
onTypeChange: (fn) => { onTypeChange = fn; fn(conntype_select.value, { select: conntype_select }); }
};
}
+export async function initializeSpecificIntegrationUI({
+ prefix,
+ promptId,
+ taLog,
+ restoreOptionsCallback
+}) {
+ const conntype_select_id = `${prefix}_connection_type`;
+ const model_prefix = `${prefix}_`;
+ const use_specific_integration_id = `${prefix}_use_specific_integration`;
+
+ // 1. Inject UI
+ try {
+ await injectConnectionUI({
+ afterTrId: 'connection_ui_anchor',
+ tr_class: 'specific_integration_sub',
+ selectId: conntype_select_id,
+ modelId_prefix: model_prefix,
+ no_chatgpt_web: true,
+ taLog: taLog
+ });
+ } catch (e) {
+ console.error(`Failed to inject connection UI (${prefix})`, e);
+ }
+
+ // 2. Restore Options
+ if (restoreOptionsCallback) {
+ await restoreOptionsCallback();
+ }
+
+ // 3. Setup Logic
+ const use_specific_integration_el = document.getElementById(use_specific_integration_id);
+ const conntype_el = document.getElementById(conntype_select_id);
+ const conntype_row = document.getElementById(conntype_select_id + '_tr');
+ const conntype_end_el = document.getElementById('connection_ui_end');
+
+ // Helper to update prompt
+ const _updatePrompt = async () => {
+ let conntype = conntype_el.value;
+
+ let prompt = await loadPrompt(promptId);
+ if(!prompt) return;
+
+ prompt.api = conntype;
+
+ for (const [integration, options] of Object.entries(integration_options_config)) {
+ for (const key of Object.keys(options)) {
+ let propName = `${integration}_${key}`;
+ let elementId = `${model_prefix}${propName}`;
+ let element = document.getElementById(elementId);
+ if (element) {
+ prompt[propName] = (element.type === 'checkbox') ? element.checked : element.value;
+ }
+ }
+ }
+
+ await savePrompt(prompt);
+ };
+
+ // Helper for visibility
+ const _updateVisibility = (checked) => {
+ document.querySelectorAll(".specific_integration_sub").forEach(tr => {
+ tr.style.display = checked && tr.classList.contains('conntype_' + conntype_el.value) ? 'table-row' : 'none';
+ });
+ if (conntype_row) conntype_row.style.display = checked ? 'table-row' : 'none';
+ if (conntype_end_el) conntype_end_el.style.display = checked ? 'table-row' : 'none';
+ if (conntype_row) changeConnTypeRowColor(conntype_row, conntype_el);
+ };
+
+ // Check global connection type
+ let globalPrefs = await browser.storage.sync.get({ connection_type: 'chatgpt_web' });
+ if (globalPrefs.connection_type === 'chatgpt_web') {
+ use_specific_integration_el.checked = true;
+ use_specific_integration_el.disabled = true;
+ }
+
+ // Event Listener for Checkbox
+ use_specific_integration_el.addEventListener('change', async (event) => {
+ _updateVisibility(event.target.checked);
+ if (!event.target.checked) {
+ await clearPromptAPI(promptId);
+ } else {
+ await _updatePrompt();
+ }
+ });
+
+ // Event Listeners for Inputs
+ conntype_el.addEventListener('change', async () => {
+ _updateVisibility(use_specific_integration_el.checked);
+ if (use_specific_integration_el.checked) await _updatePrompt();
+ });
+
+ document.querySelectorAll(".specific_integration_sub .option-input").forEach(element => {
+ element.addEventListener("change", async () => {
+ if (use_specific_integration_el.checked) await _updatePrompt();
+ });
+ });
+
+ // Initial State Apply
+ _updateVisibility(use_specific_integration_el.checked);
+ if (use_specific_integration_el.checked) {
+ await _updatePrompt();
+ }
+
+ updateWarnings(model_prefix);
+}
// From here there are exported functions
@@ -900,7 +1106,7 @@ export function changeConnTypeRowColor(conntype_row, conntype_select) {
conntype_row.classList.toggle("conntype_anthropic_api", (conntype_select.value === "anthropic_api"));
}
-export function showConnectionOptions(conntype_select) {
+export function showConnectionOptions(conntype_select, modelId_prefix = '') {
let chatgpt_web_display = 'table-row';
let chatgpt_api_display = 'none';
let ollama_api_display = 'none';
@@ -939,27 +1145,29 @@ export function showConnectionOptions(conntype_select) {
}else{
anthropic_api_display = 'none';
}
- document.querySelectorAll(".conntype_chatgpt_web").forEach(element => {
+ parent.parentElement.querySelectorAll(".conntype_chatgpt_web").forEach(element => {
element.style.display = chatgpt_web_display;
});
- document.querySelectorAll(".conntype_chatgpt_api").forEach(element => {
+ parent.parentElement.querySelectorAll(".conntype_chatgpt_api").forEach(element => {
element.style.display = chatgpt_api_display;
});
- document.querySelectorAll(".conntype_ollama_api").forEach(element => {
+ parent.parentElement.querySelectorAll(".conntype_ollama_api").forEach(element => {
element.style.display = ollama_api_display;
});
- document.querySelectorAll(".conntype_openai_comp_api").forEach(element => {
+ parent.parentElement.querySelectorAll(".conntype_openai_comp_api").forEach(element => {
element.style.display = openai_comp_api_display;
});
- document.querySelectorAll(".conntype_google_gemini_api").forEach(element => {
+ parent.parentElement.querySelectorAll(".conntype_google_gemini_api").forEach(element => {
element.style.display = google_gemini_api_display;
});
- document.querySelectorAll(".conntype_anthropic_api").forEach(element => {
+ parent.parentElement.querySelectorAll(".conntype_anthropic_api").forEach(element => {
element.style.display = anthropic_api_display;
});
if (varConnectionUI.permission_all_urls) {
- document.getElementById('openai_comp_api_cors_warning').style.display = 'none';
- document.getElementById('ollama_api_cors_warning').style.display = 'none';
+ const openaiCompWarning = parent.parentElement.getElementById((modelId_prefix ? modelId_prefix : '') + 'openai_comp_api_cors_warning');
+ if (openaiCompWarning) openaiCompWarning.style.display = 'none';
+ const ollamaWarning = parent.parentElement.getElementById((modelId_prefix ? modelId_prefix : '') + 'ollama_api_cors_warning');
+ if (ollamaWarning) ollamaWarning.style.display = 'none';
}
}
@@ -999,9 +1207,21 @@ function populateConnectionTypeOptions(selectId, no_chatgpt_web = false) {
}
}
+function warn_InvalidNumber(event){
+ const elementValue = event.target.value;
+ // console.log(">>>>>>>>>>> warn_InvalidNumber: " + event.target.id + ": " + elementValue)
+ if (elementValue != '' && isNaN(parseFloat(elementValue))) {
+ // Handle invalid number case, e.g., set border to red
+ event.target.style.border = '2px solid red';
+ } else {
+ event.target.style.border = '';
+ }
+}
+
function warn_ChatGPT_APIKeyEmpty(modelId_prefix) {
- let apiKeyInput = document.getElementById('chatgpt_api_key');
- let btnFetchChatGPTModels = document.getElementById('btnUpdateChatGPTModels');
+ const getPrefixedId = (id) => `${modelId_prefix ? `${modelId_prefix}` : ''}${id}`;
+ let apiKeyInput = document.getElementById(getPrefixedId('chatgpt_api_key'));
+ let btnFetchChatGPTModels = document.getElementById(getPrefixedId('btnUpdateChatGPTModels'));
let modelChatGPT = getModelEl('chatgpt_model', modelId_prefix);
if(apiKeyInput.value === ''){
apiKeyInput.style.border = '2px solid red';
@@ -1022,8 +1242,9 @@ function warn_ChatGPT_APIKeyEmpty(modelId_prefix) {
}
function warn_GoogleGemini_APIKeyEmpty(modelId_prefix) {
- let apiKeyInput = document.getElementById('google_gemini_api_key');
- let btnFetchGoogleGeminiModels = document.getElementById('btnUpdateGoogleGeminiModels');
+ const getPrefixedId = (id) => `${modelId_prefix ? `${modelId_prefix}` : ''}${id}`;
+ let apiKeyInput = document.getElementById(getPrefixedId('google_gemini_api_key'));
+ let btnFetchGoogleGeminiModels = document.getElementById(getPrefixedId('btnUpdateGoogleGeminiModels'));
let modelGoogleGemini = getModelEl('google_gemini_model', modelId_prefix);
if(apiKeyInput.value === ''){
apiKeyInput.style.border = '2px solid red';
@@ -1044,8 +1265,9 @@ function warn_GoogleGemini_APIKeyEmpty(modelId_prefix) {
}
function warn_Ollama_HostEmpty(modelId_prefix) {
- let hostInput = document.getElementById('ollama_host');
- let btnFetchOllamaModels = document.getElementById('btnUpdateOllamaModels');
+ const getPrefixedId = (id) => `${modelId_prefix ? `${modelId_prefix}` : ''}${id}`;
+ let hostInput = document.getElementById(getPrefixedId('ollama_host'));
+ let btnFetchOllamaModels = document.getElementById(getPrefixedId('btnUpdateOllamaModels'));
let modelOllama = getModelEl('ollama_model', modelId_prefix);
if(hostInput.value === ''){
hostInput.style.border = '2px solid red';
@@ -1066,8 +1288,9 @@ function warn_Ollama_HostEmpty(modelId_prefix) {
}
function warn_OpenAIComp_HostEmpty(modelId_prefix) {
- let hostInput = document.getElementById('openai_comp_host');
- let btnUpdateOpenAICompModels = document.getElementById('btnUpdateOpenAICompModels');
+ const getPrefixedId = (id) => `${modelId_prefix ? `${modelId_prefix}` : ''}${id}`;
+ let hostInput = document.getElementById(getPrefixedId('openai_comp_host'));
+ let btnUpdateOpenAICompModels = document.getElementById(getPrefixedId('btnUpdateOpenAICompModels'));
let modelOpenAIComp = getModelEl('openai_comp_model', modelId_prefix);
if(hostInput.value === ''){
hostInput.style.border = '2px solid red';
@@ -1088,8 +1311,9 @@ function warn_OpenAIComp_HostEmpty(modelId_prefix) {
}
function warn_Anthropic_APIKeyEmpty(modelId_prefix) {
- let apiKeyInput = document.getElementById('anthropic_api_key');
- let btnFetchAnthropicModels = document.getElementById('btnUpdateAnthropicModels');
+ const getPrefixedId = (id) => `${modelId_prefix ? `${modelId_prefix}` : ''}${id}`;
+ let apiKeyInput = document.getElementById(getPrefixedId('anthropic_api_key'));
+ let btnFetchAnthropicModels = document.getElementById(getPrefixedId('btnUpdateAnthropicModels'));
let modelAnthropic = getModelEl('anthropic_model', modelId_prefix);
if(apiKeyInput.value === ''){
apiKeyInput.style.border = '2px solid red';
@@ -1110,8 +1334,9 @@ function warn_Anthropic_APIKeyEmpty(modelId_prefix) {
}
function warn_Anthropic_VersionEmpty(modelId_prefix) {
- let versionInput = document.getElementById('anthropic_version');
- let btnFetchAnthropicModels = document.getElementById('btnUpdateAnthropicModels');
+ const getPrefixedId = (id) => `${modelId_prefix ? `${modelId_prefix}` : ''}${id}`;
+ let versionInput = document.getElementById(getPrefixedId('anthropic_version'));
+ let btnFetchAnthropicModels = document.getElementById(getPrefixedId('btnUpdateAnthropicModels'));
let modelAnthropic = getModelEl('anthropic_model', modelId_prefix);
if(versionInput.value === ''){
versionInput.style.border = '2px solid red';
@@ -1131,13 +1356,13 @@ function warn_Anthropic_VersionEmpty(modelId_prefix) {
}
}
-function resetOpenAICompConfigs(){
- let select_openai_comp_model = document.getElementById('openai_comp_services_shortcut');
+function resetOpenAICompConfigs(modelId_prefix = ''){
+ let select_openai_comp_model = document.getElementById((modelId_prefix ? modelId_prefix : '') + 'openai_comp_services_shortcut');
select_openai_comp_model.value = 'custom';
}
-function loadOpenAICompConfigs(){
- let select_openai_comp_model = document.getElementById('openai_comp_services_shortcut');
+function loadOpenAICompConfigs(modelId_prefix = ''){
+ let select_openai_comp_model = document.getElementById((modelId_prefix ? modelId_prefix : '') + 'openai_comp_services_shortcut');
openAICompConfigs.forEach(config => {
const option = document.createElement('option');
option.value = config.id;
diff --git a/pages/addtags/mzta-add-tags.js b/pages/addtags/mzta-add-tags.js
index 570dd5ec..a39bd217 100644
--- a/pages/addtags/mzta-add-tags.js
+++ b/pages/addtags/mzta-add-tags.js
@@ -16,16 +16,16 @@
* along with this program. If not, see .
*/
-import { prefs_default } from '../../options/mzta-options-default.js';
+import { prefs_default, integration_options_config } from '../../options/mzta-options-default.js';
import { taLogger } from '../../js/mzta-logger.js';
import {
getSpecialPrompts,
- setSpecialPrompts,
- loadPrompt,
- savePrompt,
- clearPromptAPI
+ setSpecialPrompts
} from "../../js/mzta-prompts.js";
-import { getPlaceholders } from "../../js/mzta-placeholders.js";
+import {
+ getPlaceholders,
+ mapPlaceholderToSuggestion
+ } from "../../js/mzta-placeholders.js";
import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js";
import {
addTags_getExclusionList,
@@ -37,84 +37,50 @@ import {
isAPIKeyValue
} from "../../js/mzta-utils.js";
import {
- injectConnectionUI,
- updateWarnings,
- changeConnTypeRowColor
+ initializeSpecificIntegrationUI
} from "../_lib/connection-ui.js";
let autocompleteSuggestions = [];
let taLog = new taLogger("mzta-addtags-page",true);
-let conntype_select_id = 'add_tags_connection_type';
-let model_prefix = 'add_tags_';
document.addEventListener('DOMContentLoaded', async () => {
- try {
- await injectConnectionUI({
- afterTrId: 'connection_ui_anchor',
- tr_class: 'specific_integration_sub',
- selectId: conntype_select_id,
- modelId_prefix: model_prefix,
- no_chatgpt_web: true,
- taLog: taLog
- });
- } catch (e) {
- console.error('Failed to inject connection UI (add-tags)', e);
+
+ let specialPrompts = await getSpecialPrompts();
+ let addtags_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_add_tags');
+
+ if (addtags_prompt && addtags_prompt.api && addtags_prompt.api !== '') {
+ let update_prefs = {};
+ update_prefs['add_tags_connection_type'] = addtags_prompt.api;
+
+ let integration = addtags_prompt.api.replace('_api', '');
+ if (integration_options_config && integration_options_config[integration]) {
+ for (const key of Object.keys(integration_options_config[integration])) {
+ if (addtags_prompt[key] !== undefined) {
+ update_prefs[`add_tags_${integration}_${key}`] = addtags_prompt[key];
+ }
+ }
+ }
+ await browser.storage.sync.set(update_prefs);
}
+
+ await initializeSpecificIntegrationUI({
+ prefix: 'add_tags',
+ promptId: 'prompt_add_tags',
+ taLog: taLog,
+ restoreOptionsCallback: restoreOptions
+ });
+
i18n.updateDocument();
- await restoreOptions();
document.querySelectorAll(".option-input").forEach(element => {
element.addEventListener("change", saveOptions);
});
-
- document.querySelectorAll(".option-input-model").forEach(element => {
- element.addEventListener("change", updatePromptAPIInfo);
- });
-
- let conntype_el = document.getElementById(conntype_select_id);
- let conntype_end_el = document.getElementById('connection_ui_end');
-
- conntype_el.addEventListener('change', updatePromptAPIInfo);
-
- let add_tags_use_specific_integration_el = document.getElementById('add_tags_use_specific_integration');
let prefs_add_tags = await browser.storage.sync.get({ add_tags_enabled_accounts: [], connection_type: 'chatgpt_web' });
- if(prefs_add_tags.connection_type == 'chatgpt_web'){
- add_tags_use_specific_integration_el.checked = true;
- add_tags_use_specific_integration_el.dispatchEvent(new Event('change'));
- add_tags_use_specific_integration_el.disabled = true;
- }
-
- let conntype_row = document.getElementById(conntype_select_id + '_tr');
- add_tags_use_specific_integration_el.addEventListener('change', (event) => {
- // console.log(">>>>>>>>>>>>> conntype_el.value: " + conntype_el.value);
- document.querySelectorAll(".specific_integration_sub").forEach(tr => {
- tr.style.display = event.target.checked && tr.classList.contains('conntype_' + conntype_el.value) ? 'table-row' : 'none';
- });
- conntype_el.style.display = event.target.checked ? 'table-row' : 'none';
- conntype_end_el.style.display = event.target.checked ? 'table-row' : 'none';
- changeConnTypeRowColor(conntype_row, conntype_el);
- if(!event.target.checked){
- clearPromptAPI('prompt_add_tags');
- }else{
- updatePromptAPIInfo();
- }
- });
-
- // console.log(">>>>>>>>>>>>> conntype_el.value: " + conntype_el.value);
- document.querySelectorAll(".specific_integration_sub").forEach(tr => {
- tr.style.display = add_tags_use_specific_integration_el.checked && tr.classList.contains('conntype_' + conntype_el.value) ? 'table-row' : 'none';
- });
- document.getElementById(conntype_select_id + '_tr').style.display = add_tags_use_specific_integration_el.checked ? 'table-row' : 'none';
- conntype_end_el.style.display = add_tags_use_specific_integration_el.checked ? 'table-row' : 'none';
- changeConnTypeRowColor(conntype_row, conntype_el);
let addtags_textarea = document.getElementById('addtags_prompt_text');
let addtags_save_btn = document.getElementById('btn_save_prompt');
let addtags_reset_btn = document.getElementById('btn_reset_prompt');
- let specialPrompts = await getSpecialPrompts();
- let addtags_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_add_tags');
-
addtags_textarea.addEventListener('input', (event) => {
addtags_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_add_tags_full_text'));
addtags_save_btn.disabled = (event.target.value === addtags_prompt.text);
@@ -177,7 +143,7 @@ document.addEventListener('DOMContentLoaded', async () => {
updateAdditionalPromptStatements();
- autocompleteSuggestions = (await getPlaceholders(true)).filter(p => !(p.id === 'additional_text')).map(p => ({command: '{%'+p.id+'%}', type: p.type}));
+ autocompleteSuggestions = (await getPlaceholders(true)).filter(p => !(p.id === 'additional_text')).map(mapPlaceholderToSuggestion);
textareaAutocomplete(addtags_textarea, autocompleteSuggestions, 1); // type_value = 1, only when reading an email
let excl_list_textarea = document.getElementById('addtags_excl_list');
@@ -257,8 +223,6 @@ document.addEventListener('DOMContentLoaded', async () => {
let checkboxes = document.querySelectorAll('.accountCheckbox');
checkboxes.forEach(checkbox => checkbox.checked = false);
});
-
- updateWarnings(model_prefix);
});
@@ -290,18 +254,6 @@ async function updateAdditionalPromptStatements(){
}
}
-async function updatePromptAPIInfo(){
- let conntype = document.getElementById(conntype_select_id).value;
- let model_value = conntype.substring(0, conntype.length - 4) + '_model';
- let model = document.getElementById(model_prefix + model_value).value;
- // console.log(">>>>>>>>>>> updatePromptAPIInfo: conntype: " + conntype + " - model: " + model + " - model_value: " + model_value);
- let add_tags_prompt = await loadPrompt('prompt_add_tags');
- // console.log(">>>>>>>>>>> updatePromptAPIInfo: BEFORE add_tags_prompt: " + JSON.stringify(add_tags_prompt));
- add_tags_prompt.api = conntype;
- add_tags_prompt.model = model;
- // console.log(">>>>>>>>>>> updatePromptAPIInfo: AFTER add_tags_prompt: " + JSON.stringify(add_tags_prompt));
- await savePrompt(add_tags_prompt);
-}
// Methods to manage options, derived from: /options/mzta-options.js
@@ -387,5 +339,23 @@ async function restoreOptions() {
}
let getting = await browser.storage.sync.get(prefs_default);
+
+ let specialPrompts = await getSpecialPrompts();
+ let addtags_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_add_tags');
+
+ if (addtags_prompt) {
+ if (addtags_prompt.api && addtags_prompt.api !== '') {
+ getting['add_tags_connection_type'] = addtags_prompt.api;
+ }
+ for (const [integration, options] of Object.entries(integration_options_config)) {
+ for (const key of Object.keys(options)) {
+ const propName = `${integration}_${key}`;
+ if (addtags_prompt[propName] !== undefined) {
+ getting[`add_tags_${propName}`] = addtags_prompt[propName];
+ }
+ }
+ }
+ }
+
setCurrentChoice(getting);
}
diff --git a/pages/customprompts/mzta-custom-prompts.css b/pages/customprompts/mzta-custom-prompts.css
index 888459df..5ff45b45 100644
--- a/pages/customprompts/mzta-custom-prompts.css
+++ b/pages/customprompts/mzta-custom-prompts.css
@@ -60,7 +60,7 @@ table.prompts_list th {
table td {
border: 1px solid #ddd;
- padding: 8px;
+ padding: 5px;
}
table td.w08{
@@ -228,11 +228,6 @@ label, .id_show, .text_show, .name_show, .type_show, .action_show{
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;
@@ -271,7 +266,7 @@ label, .id_show, .text_show, .name_show, .type_show, .action_show{
.chatgpt_web_additional_info_toggle{
width: -moz-available;
text-align: center;
- margin-top: 10px;
+ padding:5px;
}
.chatgpt_web_additional_info_show{
@@ -302,6 +297,35 @@ input.input_additional[type="text"]{
cursor: pointer;
}
+.api_additional_info{
+ cursor: pointer;
+ text-align: center;
+ background-color: #409df3;
+}
+
+.api_additional_info_toggle{
+ text-align: center;
+ padding: 5px;
+ background-color: #409df3;
+ cursor: pointer;
+ display: none;
+}
+
+.api_additional_info_show{
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ height: 100%;
+ padding: 3px;
+ margin-top: 5px;
+ background-color: rgb(64, 157, 243);
+}
+
+#api_additional_info_toggle:hover, .api_additional_info_toggle:hover{
+ background-color: rgb(64, 137, 213);
+ text-decoration: underline;
+}
+
@media (prefers-color-scheme: dark) {
body {
background-color: #1C1B22;
@@ -351,10 +375,17 @@ input.input_additional[type="text"]{
#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);
}
+
+ .api_additional_info, .api_additional_info_toggle, .api_additional_info_show{
+ background-color: #0f2437;
+ }
+
+ #api_additional_info_toggle:hover, .api_additional_info_toggle:hover{
+ background-color: #1b456d
+ }
}
\ No newline at end of file
diff --git a/pages/customprompts/mzta-custom-prompts.html b/pages/customprompts/mzta-custom-prompts.html
index cedae75c..1ec1d4bb 100644
--- a/pages/customprompts/mzta-custom-prompts.html
+++ b/pages/customprompts/mzta-custom-prompts.html
@@ -4,6 +4,7 @@
ThunderAI - __MSG_customPrompts_managePrompts__
+
@@ -79,7 +80,7 @@
- __MSG_customPrompts_show_additional_info__
+ __MSG_customPrompts_show_additional_info__ [ChatGPT Web]
__MSG_prefs_OptionText_chatgpt_web_model__:
@@ -101,6 +102,19 @@
__MSG_prefs_OptionText_chatgpt_web_custom_data_info2__
+
+
+ __MSG_customPrompts_show_additional_info__ [API]
+
+
+
+
+
+
+
* __MSG_customPrompts_form_required_fields__
diff --git a/pages/customprompts/mzta-custom-prompts.js b/pages/customprompts/mzta-custom-prompts.js
index b1e2f86a..b82086bb 100644
--- a/pages/customprompts/mzta-custom-prompts.js
+++ b/pages/customprompts/mzta-custom-prompts.js
@@ -16,11 +16,37 @@
* along with this program. If not, see .
*/
-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, openTab } from "../../js/mzta-utils.js";
+import {
+ prefs_default,
+ integration_options_config
+} from "../../options/mzta-options-default.js";
+import {
+ getPrompts,
+ setDefaultPromptsProperties,
+ setCustomPrompts,
+ preparePromptsForExport,
+ preparePromptsForImport
+} from "../../js/mzta-prompts.js";
+import {
+ injectConnectionUI,
+ showConnectionOptions,
+ updateWarnings
+} from "../../pages/_lib/connection-ui.js";
+import {
+ ChatGPTWeb_models,
+ isThunderbird128OrGreater,
+ getLocalStorageUsedSpace,
+ sanitizeHtml,
+ validateCustomData_ChatGPTWeb,
+ getChatGPTWebModelsList_HTML,
+ openTab
+} from "../../js/mzta-utils.js";
import { taLogger } from "../../js/mzta-logger.js";
-import { getPlaceholders, placeholdersUtils } from "../../js/mzta-placeholders.js";
+import {
+ getPlaceholders,
+ placeholdersUtils,
+ mapPlaceholderToSuggestion
+} from "../../js/mzta-placeholders.js";
import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js";
let prefs = null;
@@ -35,7 +61,8 @@ let autocompleteSuggestions = [];
document.addEventListener('DOMContentLoaded', async () => {
- prefs = await browser.storage.sync.get({ connection_type:prefs_default.connection_type, do_debug: prefs_default.do_debug });
+ let storedPrefs = await browser.storage.sync.get(null);
+ prefs = { ...prefs_default, ...storedPrefs };
taLog = new taLogger("mzta-custom-prompts", prefs.do_debug);
setStorageSpace();
@@ -86,7 +113,7 @@ document.addEventListener('DOMContentLoaded', async () => {
}
const textareas = document.querySelectorAll('.editor');
- autocompleteSuggestions = (await getPlaceholders(true)).map(p => ({command: '{%'+p.id+'%}', type: p.type}));
+ autocompleteSuggestions = (await getPlaceholders(true)).map(mapPlaceholderToSuggestion);
// console.log('>>>>>>>>>>> autocompleteSuggestions: ' + JSON.stringify(autocompleteSuggestions));
@@ -100,34 +127,98 @@ document.addEventListener('DOMContentLoaded', async () => {
});
});
+ const apiSettingsToggle = document.getElementById('api_additional_info_toggle');
+ const apiSettingsRow = document.getElementById('api_additional_info');
+
+ apiSettingsToggle.addEventListener('click', (e) => {
+ e.preventDefault();
+ if (apiSettingsRow.style.display === 'none') {
+ apiSettingsRow.style.display = 'table-row';
+ apiSettingsToggle.querySelector('span').innerText = browser.i18n.getMessage('customPrompts_hide_additional_info') + ' [API]';
+ } else {
+ apiSettingsRow.style.display = 'none';
+ apiSettingsToggle.querySelector('span').innerText = browser.i18n.getMessage('customPrompts_show_additional_info') + ' [API]';
+ }
+ });
+
+ await injectConnectionUI({
+ afterTrId: 'api_ui_anchor',
+ selectId: 'new_prompt_api_type',
+ no_chatgpt_web: true,
+ taLog: taLog
+ });
+
+ // Fill defaults for new prompt form
+ for (const [integration, options] of Object.entries(integration_options_config)) {
+ for (const key of Object.keys(options)) {
+ const propName = `${integration}_${key}`;
+ const inputEl = document.getElementById(propName);
+ if (inputEl && prefs[propName] !== undefined) {
+ if (inputEl.type === 'checkbox') {
+ inputEl.checked = (prefs[propName] === true || prefs[propName] === 'true');
+ } else {
+ inputEl.value = prefs[propName];
+ }
+ }
+ }
+ }
+
i18n.updateDocument();
- switch(prefs.connection_type) {
- case 'chatgpt_web': {
- // for the new item form
- document.getElementById('chatgpt_web_additional_info_toggle').style.display = 'table-row';
- // for the edit list items form
- document.querySelectorAll('.chatgpt_web_additional_info_toggle').forEach(element => {
- element.addEventListener('click', (e) => {
- e.preventDefault();
- let additionalInfoRow = e.target.closest('td').querySelector('.chatgpt_web_additional_info');
- if (additionalInfoRow.style.display === 'none' || additionalInfoRow.style.display === '') {
- additionalInfoRow.style.display = 'block';
- e.target.innerText = browser.i18n.getMessage('customPrompts_hide_additional_info');
- } else {
- additionalInfoRow.style.display = 'none';
- e.target.innerText = browser.i18n.getMessage('customPrompts_show_additional_info');
- }
- });
+ const apiSelect = document.getElementById('new_prompt_api_type');
+ // Remove chatgpt_web
+ // for (let i = 0; i < apiSelect.options.length; i++) {
+ // if (apiSelect.options[i].value === 'chatgpt_web') {
+ // apiSelect.remove(i);
+ // break;
+ // }
+ // }
+ apiSelect.addEventListener('change', () => {
+ showConnectionOptions(apiSelect);
+ });
+ showConnectionOptions(apiSelect);
+
+ if(prefs.connection_type == 'chatgpt_web') {
+ // for the new item form
+ document.getElementById('chatgpt_web_additional_info_toggle').style.display = 'table-row';
+ // for the edit list items form
+ document.querySelectorAll('.chatgpt_web_additional_info_toggle').forEach(element => {
+ element.addEventListener('click', (e) => {
+ e.preventDefault();
+ let additionalInfoRow = e.target.closest('td').querySelector('.chatgpt_web_additional_info');
+ if (additionalInfoRow.style.display === 'none' || additionalInfoRow.style.display === '') {
+ additionalInfoRow.style.display = 'block';
+ e.target.innerText = browser.i18n.getMessage('customPrompts_hide_additional_info') + ' [ChatGPT Web]';
+ } else {
+ additionalInfoRow.style.display = 'none';
+ e.target.innerText = browser.i18n.getMessage('customPrompts_show_additional_info') + ' [ChatGPT Web]';
+ }
});
- document.querySelectorAll('input.chatgpt_web_project_output').forEach(element => {
- element.addEventListener("input", validateCustomData_ChatGPTWeb);
- });
- document.querySelectorAll('input.chatgpt_web_custom_gpt_output').forEach(element => {
- element.addEventListener("input", validateCustomData_ChatGPTWeb);
- });
- break;
- }
+ });
+ document.querySelectorAll('input.chatgpt_web_project_output').forEach(element => {
+ element.addEventListener("input", validateCustomData_ChatGPTWeb);
+ });
+ document.querySelectorAll('input.chatgpt_web_custom_gpt_output').forEach(element => {
+ element.addEventListener("input", validateCustomData_ChatGPTWeb);
+ });
+ }
+
+ // for the edit list items form [API]
+ document.querySelectorAll('.api_additional_info_toggle').forEach(element => {
+ element.addEventListener('click', (e) => {
+ e.preventDefault();
+ let additionalInfoRow = element.nextElementSibling;
+ if (additionalInfoRow.style.display === 'none' || additionalInfoRow.style.display === '') {
+ additionalInfoRow.style.display = 'block';
+ element.innerText = browser.i18n.getMessage('customPrompts_hide_additional_info') + ' [API]';
+ } else {
+ additionalInfoRow.style.display = 'none';
+ element.innerText = browser.i18n.getMessage('customPrompts_show_additional_info') + ' [API]';
+ }
+ });
+ });
+
+ // switch(prefs.connection_type) {
// case 'chatgpt_api':
// document.getElementById('chatgpt_api').style.display = 'block';
// break;
@@ -140,7 +231,7 @@ document.addEventListener('DOMContentLoaded', async () => {
// case 'google_gemini_api':
// document.getElementById('google_gemini_api').style.display = 'block';
// break;
- }
+ // }
const chatgptWebAdditionalPropToggle = document.getElementById('chatgpt_web_additional_info_toggle');
chatgptWebAdditionalPropToggle.addEventListener('click', (e) => {
@@ -150,13 +241,13 @@ document.addEventListener('DOMContentLoaded', async () => {
additionalInfoRow.style.display = 'table-row';
let subspan = chatgptWebAdditionalPropToggle.querySelector('td span');
if (subspan) {
- subspan.innerText = browser.i18n.getMessage('customPrompts_hide_additional_info');
+ subspan.innerText = browser.i18n.getMessage('customPrompts_hide_additional_info') + ' [ChatGPT Web]';
}
} else {
additionalInfoRow.style.display = 'none';
let subspan = chatgptWebAdditionalPropToggle.querySelector('td span');
if (subspan) {
- subspan.innerText = browser.i18n.getMessage('customPrompts_show_additional_info');
+ subspan.innerText = browser.i18n.getMessage('customPrompts_show_additional_info') + ' [ChatGPT Web]';
}
}
});
@@ -210,6 +301,7 @@ document.addEventListener('DOMContentLoaded', async () => {
position_display: positionMax_display + 1,
is_default: 0,
idnum: idnumMax + 1,
+ api_type: document.getElementById('new_prompt_api_type').value
};
switch(prefs.connection_type) {
@@ -232,6 +324,9 @@ document.addEventListener('DOMContentLoaded', async () => {
// break;
}
+ const apiValues = getAPIValuesFromUI();
+ Object.assign(newItemData, apiValues);
+
let newItem = promptsList.add(newItemData);
idnumMax++;
let curr_idnum = newItem[0].values().idnum;
@@ -344,6 +439,10 @@ document.addEventListener('DOMContentLoaded', async () => {
document.querySelectorAll('.chatgpt_web_additional_info_show').forEach(element => {
toggleAdditionalPropertiesShow(element.closest('tr'));
});
+
+ document.querySelectorAll('.api_additional_info_show').forEach(element => {
+ toggleApiPropertiesShow(element.closest('tr'));
+ });
getChatGPTWebModelsList_HTML(ChatGPTWeb_models, 'chatgpt_web_models_list');
let formNewWebModelList = document.getElementById('chatgpt_web_models_list');
@@ -377,6 +476,38 @@ document.getElementById('btnManageCustomDataPH').addEventListener('click', () =>
function handleEditClick(e) {
e.preventDefault();
const tr = e.target.parentNode.parentNode;
+ const id = tr.querySelector('.id_output').value;
+
+ // Inject Connection UI if needed
+ const anchorId = `api_ui_anchor_${id}`;
+ const selectId = `api_type_${id}`;
+ const prefix = `prompt_${id}_`;
+
+ if (!document.getElementById(selectId)) {
+ injectConnectionUI({
+ afterTrId: anchorId,
+ selectId: selectId,
+ modelId_prefix: prefix,
+ no_chatgpt_web: true,
+ taLog: taLog,
+ customButtonLabel: browser.i18n.getMessage("Reset"),
+ customButtonCallback: () => {
+ const selectEl = document.getElementById(selectId);
+ if (selectEl) {
+ selectEl.value = '';
+ selectEl.dispatchEvent(new Event('change'));
+ }
+ }
+ }).then(() => {
+ populateConnectionUI(tr, id, prefix, selectId);
+ updateWarnings(prefix);
+ });
+ } else {
+ populateConnectionUI(tr, id, prefix, selectId);
+ updateWarnings(prefix);
+ }
+
+ // Show/Hide buttons
//console.log('>>>>>>>> tr: ' + tr.getAttribute('data-idnum'));
e.target.style.display = 'none'; // Edit btn
tr.querySelector('.btnConfirmItem').style.display = 'inline'; // Save btn
@@ -387,6 +518,36 @@ function handleEditClick(e) {
toggleDiffviewer(e);
}
+function populateConnectionUI(tr, id, prefix, selectId) {
+ const item = promptsList.get('id', id)[0];
+ const itemValues = item.values();
+
+ const selectEl = document.getElementById(selectId);
+ if (selectEl) {
+ selectEl.value = itemValues.api_type || '';
+ showConnectionOptions(selectEl);
+ }
+
+ for (const [integration, options] of Object.entries(integration_options_config)) {
+ for (const key of Object.keys(options)) {
+ const propName = `${integration}_${key}`;
+ const inputId = `${prefix}${propName}`;
+ const inputEl = document.getElementById(inputId);
+ if (inputEl) {
+ let val = itemValues[propName];
+ // Use default if undefined or empty string (for text inputs)
+ if (val === undefined || (inputEl.type !== 'checkbox' && val === '')) {
+ if (prefs[propName] !== undefined) {
+ val = prefs[propName];
+ }
+ }
+ inputEl.type === 'checkbox' ? inputEl.checked = (val === true || val === 'true') : inputEl.value = val || '';
+ }
+ }
+ }
+ i18n.updateDocument();
+}
+
function showItemRowEditor(tr) {
tr.querySelector('.id_output').style.display = 'inline';
tr.querySelector('.id_show').style.display = 'none';
@@ -398,6 +559,7 @@ function showItemRowEditor(tr) {
tr.querySelector('.text_show').style.display = 'none';
toggleAdditionalPropertiesEditor(tr);
tr.querySelector('.chatgpt_web_additional_info_show').style.display = 'none';
+ tr.querySelector('.api_additional_info_show').style.display = 'none';
tr.querySelector('.type_output').style.display = 'inline';
tr.querySelector('.type_show').style.display = 'none';
const action_output = tr.querySelector('.action_output')
@@ -420,6 +582,8 @@ function hideItemRowEditor(tr) {
tr.querySelector('.text_show').style.display = 'inline';
tr.querySelector('.chatgpt_web_additional_info_toggle').style.display = 'none';
tr.querySelector('.chatgpt_web_additional_info').style.display = 'none';
+ tr.querySelector('.api_additional_info_toggle').style.display = 'none';
+ tr.querySelector('.api_additional_info').style.display = 'none';
toggleAdditionalPropertiesShow(tr);
tr.querySelector('.type_output').style.display = 'none';
tr.querySelector('.type_show').style.display = 'inline';
@@ -482,6 +646,23 @@ function toggleAdditionalPropertiesShow(tr) {
}
}
+function toggleApiPropertiesShow(tr) {
+ let element = tr.querySelector('.api_additional_info_show');
+ let api_type_show = tr.querySelector('.api_type_show');
+
+ if (api_type_show.innerText !== '' && api_type_show.innerText !== 'undefined') {
+ element.style.display = 'flex';
+ } else {
+ element.style.display = 'none';
+ }
+
+ if(api_type_show.innerText === '' || api_type_show.innerText === 'undefined') {
+ api_type_show.parentNode.style.display = 'none';
+ } else {
+ api_type_show.parentNode.style.display = 'inline';
+ }
+}
+
function toggleAdditionalPropertiesEditor(tr) {
switch(prefs.connection_type) {
case 'chatgpt_web': {
@@ -511,6 +692,14 @@ function toggleAdditionalPropertiesEditor(tr) {
// document.getElementById('google_gemini_api').style.display = 'block';
// break;
}
+
+ let api_info_toggle = tr.querySelector('.api_additional_info_toggle');
+ api_info_toggle.style.display = 'block';
+ let api_type_show = tr.querySelector('.api_type_show').innerText;
+
+ if (api_type_show !== '' && api_type_show !== 'undefined') {
+ api_info_toggle.click();
+ }
}
function toggleDiffviewer(e) {
@@ -563,6 +752,9 @@ function handleCancelClick(e) {
tr.querySelector('.chatgpt_web_model_output').value = tr.querySelector('.chatgpt_web_model_show').innerText;
tr.querySelector('.chatgpt_web_project_output').value = tr.querySelector('.chatgpt_web_project_show').innerText;
tr.querySelector('.chatgpt_web_custom_gpt_output').value = tr.querySelector('.chatgpt_web_custom_gpt_show').innerText;
+ tr.querySelector('.api_additional_info_toggle').innerText = browser.i18n.getMessage('customPrompts_show_additional_info') + ' [API]';
+ tr.querySelector('.api_additional_info_show').style.display = 'block';
+
hideItemRowEditor(tr);
}
@@ -570,21 +762,51 @@ function handleConfirmClick(e) {
e.preventDefault();
const tr = e.target.parentNode.parentNode;
e.target.style.display = 'none'; // Ok btn
+
+ const oldId = tr.querySelector('.id_show').innerText;
+ const prefix = `prompt_${oldId}_`;
+ const selectId = `api_type_${oldId}`;
+
+ let newValues = {};
+
+ // Standard fields
+ newValues.id = tr.querySelector('.id_output').value.trim().toLowerCase();
+ newValues.name = tr.querySelector('.name_output').value.trim();
+ newValues.text = tr.querySelector('.text_output').value;
+ newValues.type = tr.querySelector('.type_output').value;
+ newValues.action = tr.querySelector('.action_output').value;
+ newValues.need_selected = tr.querySelector('.need_selected').checked ? 1 : 0;
+ newValues.need_signature = tr.querySelector('.need_signature').checked ? 1 : 0;
+ newValues.need_custom_text = tr.querySelector('.need_custom_text').checked ? 1 : 0;
+ newValues.define_response_lang = tr.querySelector('.define_response_lang').checked ? 1 : 0;
+ newValues.use_diff_viewer = tr.querySelector('.use_diff_viewer').checked ? 1 : 0;
+ newValues.enabled = tr.querySelector('.enabled').checked ? 1 : 0;
+ newValues.chatgpt_web_model = tr.querySelector('.chatgpt_web_model_output').value.trim();
+ newValues.chatgpt_web_project = tr.querySelector('.chatgpt_web_project_output').value.trim();
+ newValues.chatgpt_web_custom_gpt = tr.querySelector('.chatgpt_web_custom_gpt_output').value.trim();
+
+ const selectEl = document.getElementById(selectId);
+ if(selectEl) newValues.api_type = selectEl.value;
+
+ const apiValues = getAPIValuesFromUI(prefix);
+ Object.assign(newValues, apiValues);
+ promptsList.get('id', oldId)[0].values(newValues);
+
// 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('.chatgpt_web_model_show').innerText = tr.querySelector('.chatgpt_web_model_output').value;
- tr.querySelector('.chatgpt_web_project_show').innerText = tr.querySelector('.chatgpt_web_project_output').value;
- tr.querySelector('.chatgpt_web_custom_gpt_show').innerText = tr.querySelector('.chatgpt_web_custom_gpt_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;
+ if (newValues.api_type !== '') {
+ tr.querySelector('.api_type_show').innerText = newValues.api_type;
+ tr.querySelector('.api_additional_info_show').style.display = 'block';
+ tr.querySelector('.api_additional_info_row').style.display = 'block';
+
+ }
// the checkboxes update is handled directly by themselves
hideItemRowEditor(tr);
setSomethingChanged();
@@ -613,8 +835,15 @@ function handleInputChange(e) {
function loadPromptsList(values){
// console.log('>>>>>>>> loadPromptsList values: ' + JSON.stringify(values));
+ let api_fields = [];
+ for (const [integration, options] of Object.entries(integration_options_config)) {
+ for (const key of Object.keys(options)) {
+ api_fields.push(`${integration}_${key}`);
+ }
+ }
+
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: 'define_response_lang', attr: 'checked_val'}, { name: 'use_diff_viewer', attr: 'checked_val'}, { name: 'enabled', attr: 'checked_val'} ],
+ 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: 'define_response_lang', attr: 'checked_val'}, { name: 'use_diff_viewer', attr: 'checked_val'}, { name: 'enabled', attr: 'checked_val'}, 'api_type', ...api_fields ],
item: function(values) {
let type_output = '';
switch(String(values.type)){
@@ -652,7 +881,7 @@ function loadPromptsList(values){
- __MSG_customPrompts_show_additional_info__
+ __MSG_customPrompts_show_additional_info__ [ChatGPT Web]
__MSG_prefs_OptionText_chatgpt_web_model__:
@@ -672,6 +901,14 @@ function loadPromptsList(values){
__MSG_prefs_OptionText_chatgpt_web_custom_data_info2__
__MSG_prefs_OptionText_CustomGPT_Warn__
+ __MSG_customPrompts_show_additional_info__ [API]
+
__MSG_customPrompts_add_to_menu__:
@@ -707,11 +944,14 @@ function loadPromptsList(values){
- __MSG_customPrompts_show_additional_info_show__
+
__MSG_customPrompts_show_additional_info_show__ [ChatGPT Web]
__MSG_prefs_OptionText_chatgpt_web_model__: ` + values.chatgpt_web_model + `
__MSG_prefs_OptionText_chatgpt_web_project__: ` + values.chatgpt_web_project + `
__MSG_prefs_OptionText_chatgpt_web_custom_gpt__: ` + values.chatgpt_web_custom_gpt + `
+
+
__MSG_prefs_Connection_type__: ` + values.api_type + `
+
__MSG_customPrompts_btnEdit__
@@ -836,6 +1076,21 @@ function clearFields() {
document.getElementById('formNew').style.display = 'none';
}
+function getAPIValuesFromUI(prefix = '') {
+ let values = {};
+ for (const [integration, options] of Object.entries(integration_options_config)) {
+ for (const key of Object.keys(options)) {
+ const propName = `${integration}_${key}`;
+ const inputId = `${prefix}${propName}`;
+ const inputEl = document.getElementById(inputId);
+ if (inputEl) {
+ values[propName] = (inputEl.type === 'checkbox') ? inputEl.checked : inputEl.value;
+ }
+ }
+ }
+ return values;
+}
+
function inputSetError(input) {
document.getElementById(input).style.borderColor = 'red';
}
@@ -893,8 +1148,6 @@ 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
// console.log(">>>>>>>>>>>>>>>> item: " + JSON.stringify(item))
diff --git a/pages/get-calendar-event/mzta-get-calendar-event.js b/pages/get-calendar-event/mzta-get-calendar-event.js
index 455e12d4..303ac444 100644
--- a/pages/get-calendar-event/mzta-get-calendar-event.js
+++ b/pages/get-calendar-event/mzta-get-calendar-event.js
@@ -19,7 +19,10 @@
import { prefs_default } from '../../options/mzta-options-default.js';
import { taLogger } from '../../js/mzta-logger.js';
import { getSpecialPrompts, setSpecialPrompts } from "../../js/mzta-prompts.js";
-import { getPlaceholders } from "../../js/mzta-placeholders.js";
+import {
+ getPlaceholders,
+ mapPlaceholderToSuggestion
+} from "../../js/mzta-placeholders.js";
import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js";
import { isAPIKeyValue } from "../../js/mzta-utils.js";
@@ -73,7 +76,7 @@ document.addEventListener('DOMContentLoaded', async () => {
get_calendar_event_textarea.value = get_calendar_event_prompt.text;
get_calendar_event_reset_btn.disabled = (get_calendar_event_textarea.value === browser.i18n.getMessage('prompt_get_calendar_event_full_text'));
- autocompleteSuggestions = (await getPlaceholders(true)).filter(p => !(p.id === 'additional_text')).map(p => ({command: '{%'+p.id+'%}', type: p.type}));
+ autocompleteSuggestions = (await getPlaceholders(true)).filter(p => !(p.id === 'additional_text')).map(mapPlaceholderToSuggestion);
textareaAutocomplete(get_calendar_event_textarea, autocompleteSuggestions, 1); // type_value = 1, only when reading an email
});
diff --git a/pages/get-task/mzta-get-task.js b/pages/get-task/mzta-get-task.js
index da7c0fee..84df2e8e 100644
--- a/pages/get-task/mzta-get-task.js
+++ b/pages/get-task/mzta-get-task.js
@@ -19,7 +19,10 @@
import { prefs_default } from '../../options/mzta-options-default.js';
import { taLogger } from '../../js/mzta-logger.js';
import { getSpecialPrompts, setSpecialPrompts } from "../../js/mzta-prompts.js";
-import { getPlaceholders } from "../../js/mzta-placeholders.js";
+import {
+ getPlaceholders,
+ mapPlaceholderToSuggestion
+} from "../../js/mzta-placeholders.js";
import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js";
import { isAPIKeyValue } from "../../js/mzta-utils.js";
@@ -73,7 +76,7 @@ document.addEventListener('DOMContentLoaded', async () => {
get_calendar_event_textarea.value = get_calendar_event_prompt.text;
get_calendar_event_reset_btn.disabled = (get_calendar_event_textarea.value === browser.i18n.getMessage('prompt_get_task_full_text'));
- autocompleteSuggestions = (await getPlaceholders(true)).filter(p => !(p.id === 'additional_text')).map(p => ({command: '{%'+p.id+'%}', type: p.type}));
+ autocompleteSuggestions = (await getPlaceholders(true)).filter(p => !(p.id === 'additional_text')).map(mapPlaceholderToSuggestion);
textareaAutocomplete(get_calendar_event_textarea, autocompleteSuggestions, 1); // type_value = 1, only when reading an email
});
diff --git a/pages/spamfilter/mzta-spamfilter.js b/pages/spamfilter/mzta-spamfilter.js
index a783d3ca..a6f7d30e 100644
--- a/pages/spamfilter/mzta-spamfilter.js
+++ b/pages/spamfilter/mzta-spamfilter.js
@@ -16,43 +16,40 @@
* along with this program. If not, see .
*/
-import { prefs_default } from '../../options/mzta-options-default.js';
+import { prefs_default, integration_options_config } from '../../options/mzta-options-default.js';
import { taLogger } from '../../js/mzta-logger.js';
-import { getSpecialPrompts, setSpecialPrompts, loadPrompt, savePrompt, clearPromptAPI } from "../../js/mzta-prompts.js";
-import { getPlaceholders } from "../../js/mzta-placeholders.js";
+import {
+ getSpecialPrompts,
+ setSpecialPrompts
+} from "../../js/mzta-prompts.js";
+import {
+ getPlaceholders,
+ mapPlaceholderToSuggestion
+} from "../../js/mzta-placeholders.js";
import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js";
import { taSpamReport } from '../../js/mzta-spamreport.js';
import { getAccountsList, isAPIKeyValue } from "../../js/mzta-utils.js";
import {
- injectConnectionUI,
- updateWarnings,
- changeConnTypeRowColor
+ initializeSpecificIntegrationUI
} from "../_lib/connection-ui.js";
let autocompleteSuggestions = [];
let taLog = new taLogger("mzta-spamfilter-page",true);
taSpamReport.logger = taLog;
-let conntype_select_id = 'spamfilter_connection_type';
-let model_prefix = 'spamfilter_';
-
document.addEventListener('DOMContentLoaded', async () => {
- try {
- await injectConnectionUI({
- afterTrId: 'connection_ui_anchor',
- tr_class: 'specific_integration_sub',
- selectId: conntype_select_id,
- modelId_prefix: model_prefix,
- no_chatgpt_web: true,
- taLog: taLog
- });
- } catch (e) {
- console.error('Failed to inject connection UI (spamfilter)', e);
- }
+ let specialPrompts = await getSpecialPrompts();
+ let spamfilter_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_spamfilter');
+
+ await initializeSpecificIntegrationUI({
+ prefix: 'spamfilter',
+ promptId: 'prompt_spamfilter',
+ taLog: taLog,
+ restoreOptionsCallback: restoreOptions
+ });
i18n.updateDocument();
- await restoreOptions();
document.querySelectorAll(".option-input").forEach(element => {
element.addEventListener("change", saveOptions);
@@ -61,55 +58,11 @@ document.addEventListener('DOMContentLoaded', async () => {
document.getElementById("spamfilter_threshold").addEventListener("input", check_spamfilter_threshold);
check_spamfilter_threshold({target: document.getElementById("spamfilter_threshold")});
- // Bind prompt API updates to connection type and model selects
- document.querySelectorAll(".option-input-model").forEach(element => {
- element.addEventListener("change", updatePromptAPIInfo);
- });
- const conntype_el = document.getElementById(conntype_select_id);
- if (conntype_el) {
- conntype_el.addEventListener('change', updatePromptAPIInfo);
- const conntype_row = document.getElementById(conntype_select_id + '_tr');
- if (conntype_row) changeConnTypeRowColor(conntype_row, conntype_el);
- }
-
- // Specific integration toggle behavior
- const spamfilter_use_specific_integration_el = document.getElementById('spamfilter_use_specific_integration');
- let prefs_spamfilter_init = await browser.storage.sync.get({ spamfilter_enabled_accounts: [], connection_type: 'chatgpt_web' });
- if(prefs_spamfilter_init.connection_type == 'chatgpt_web'){
- spamfilter_use_specific_integration_el.checked = true;
- spamfilter_use_specific_integration_el.dispatchEvent(new Event('change'));
- spamfilter_use_specific_integration_el.disabled = true;
- }
- const conntype_end_el = document.getElementById('connection_ui_end');
- const conntype_row = document.getElementById(conntype_select_id + '_tr');
- spamfilter_use_specific_integration_el.addEventListener('change', async (event) => {
- document.querySelectorAll('.specific_integration_sub').forEach(tr => {
- tr.style.display = event.target.checked && tr.classList.contains('conntype_' + conntype_el.value) ? 'table-row' : 'none';
- });
- if (conntype_row) conntype_row.style.display = event.target.checked ? 'table-row' : 'none';
- if (conntype_end_el) conntype_end_el.style.display = event.target.checked ? 'table-row' : 'none';
- if(!event.target.checked){
- await clearPromptAPI('prompt_spamfilter');
- }else{
- updatePromptAPIInfo();
- }
- if (conntype_row) changeConnTypeRowColor(conntype_row, conntype_el);
- });
-
- // Initialize visibility per current toggle value
- document.querySelectorAll('.specific_integration_sub').forEach(tr => {
- tr.style.display = spamfilter_use_specific_integration_el.checked && tr.classList.contains('conntype_' + conntype_el.value) ? 'table-row' : 'none';
- });
- if (conntype_row) conntype_row.style.display = spamfilter_use_specific_integration_el.checked ? 'table-row' : 'none';
- if (conntype_end_el) conntype_end_el.style.display = spamfilter_use_specific_integration_el.checked ? 'table-row' : 'none';
let spamfilter_textarea = document.getElementById('spamfilter_prompt_text');
let spamfilter_save_btn = document.getElementById('btn_save_prompt');
let spamfilter_reset_btn = document.getElementById('btn_reset_prompt');
- let specialPrompts = await getSpecialPrompts();
- let spamfilter_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_spamfilter');
-
spamfilter_textarea.addEventListener('input', (event) => {
spamfilter_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_spamfilter_full_text'));
spamfilter_save_btn.disabled = (event.target.value === spamfilter_prompt.text);
@@ -141,7 +94,7 @@ document.addEventListener('DOMContentLoaded', async () => {
spamfilter_textarea.value = spamfilter_prompt.text;
spamfilter_reset_btn.disabled = (spamfilter_textarea.value === browser.i18n.getMessage('prompt_spamfilter_full_text'));
- autocompleteSuggestions = (await getPlaceholders(true)).filter(p => !(p.id === 'additional_text')).map(p => ({command: '{%'+p.id+'%}', type: p.type}));
+ autocompleteSuggestions = (await getPlaceholders(true)).filter(p => !(p.id === 'additional_text')).map(mapPlaceholderToSuggestion);
textareaAutocomplete(spamfilter_textarea, autocompleteSuggestions, 1); // type_value = 1, only when reading an email
//Accounts manager
@@ -199,9 +152,6 @@ document.addEventListener('DOMContentLoaded', async () => {
});
loadSpamReport();
- updateWarnings(model_prefix);
- // Sync prompt API/model once on load
- updatePromptAPIInfo();
});
function check_spamfilter_threshold(event) {
@@ -231,20 +181,6 @@ async function loadSpamReport(){
}
}
-async function updatePromptAPIInfo(){
- const conntypeEl = document.getElementById(conntype_select_id);
- if (!conntypeEl || !conntypeEl.value) return;
- const conntype = conntypeEl.value;
- const model_value = conntype.substring(0, conntype.length - 4) + '_model';
- const modelEl = document.getElementById(model_prefix + model_value);
- if (!modelEl) return;
- const model = modelEl.value;
- let spamfilter_prompt = await loadPrompt('prompt_spamfilter');
- if (!spamfilter_prompt) return;
- spamfilter_prompt.api = conntype;
- spamfilter_prompt.model = model;
- await savePrompt(spamfilter_prompt);
-}
// Function to populate the table
function populateTable(data) {
@@ -372,5 +308,23 @@ async function restoreOptions() {
}
let getting = await browser.storage.sync.get(prefs_default);
+
+ let specialPrompts = await getSpecialPrompts();
+ let spamfilter_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_spamfilter');
+
+ if (spamfilter_prompt) {
+ if (spamfilter_prompt.api && spamfilter_prompt.api !== '') {
+ getting['spamfilter_connection_type'] = spamfilter_prompt.api;
+ }
+ for (const [integration, options] of Object.entries(integration_options_config)) {
+ for (const key of Object.keys(options)) {
+ const propName = `${integration}_${key}`;
+ if (spamfilter_prompt[propName] !== undefined) {
+ getting[`spamfilter_${propName}`] = spamfilter_prompt[propName];
+ }
+ }
+ }
+ }
+
setCurrentChoice(getting);
}